pax_global_header00006660000000000000000000000064137757672300014533gustar00rootroot0000000000000052 comment=029a8118f181cd705cfee943c28b0de6b46776b9 sipsak-0.9.8.1/000077500000000000000000000000001377576723000132025ustar00rootroot00000000000000sipsak-0.9.8.1/.gitignore000066400000000000000000000005151377576723000151730ustar00rootroot00000000000000# Autotools *~ .deps/ .dirstamp INSTALL Makefile Makefile.in aclocal.m4 autom4te.cache/ compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp install-sh missing mkinstalldirs stamp-h1 test-driver # Test suite *.log *.trs # Programs and objects *.o check_helper check_md5 check_auth sipsak sipsak-0.9.8.1/.travis.yml000066400000000000000000000004211377576723000153100ustar00rootroot00000000000000language: c os: - linux - osx compiler: - clang - gcc sudo: false addons: apt: packages: - check - libc-ares-dev homebrew: packages: - check update: true install: autoreconf --install script: - ./configure && make && make check sipsak-0.9.8.1/AUTHORS000066400000000000000000000012051377576723000142500ustar00rootroot00000000000000# 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), CVS account login (C), # PGP key ID and fingerprint (P), description (D), and snail-mail address (S). N: Nils Ohlmeier E: nils@sipsak.org P: 6CC9F8A1 439A E69B 212C 01E2 2D95 9AAE 636A 3F56 6CC9 F8A1 D: the original author and main developer N: Jan Janak E: jan@iptel.org D: contributed for example the forking code and several ideas N: Jiri Kuthan E: jiri@iptel.org D: contributed the exit code code and uncounted bug reports and ideas sipsak-0.9.8.1/COPYING000066400000000000000000000431311377576723000142370ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. sipsak-0.9.8.1/ChangeLog000066400000000000000000000143461377576723000147640ustar00rootroot00000000000000* version 0.9.8 - fixed issue #71 "local port is always zero" * version 0.9.8 - removed support for ruli - lots of internal refactoring to make sipsak compile with -fno-common (>= gcc-10) * version 0.9.7 - added new option -E which overrules SRV result - added new option -J to take ha1 instead of password - dont retransmit on reliable transports - added --disable-ips configure option which allows to compile the oldstyle --numeric behavior - added new option -k only available with TLS support - added 'star' as special word for the contact option -C - fixed overwritting of outbound proxy if domainname is an IP; thanks to Alexander Litvak - added option -Z to modify SIP_T1 at run time - added syslog support - enabled -c parameter for Inivte mode - added new options for TLS (ca-cert, client-cert, ignore-cert-failure) Note: these options are only available as long options - added option to ignore TLS certificate verification errors - added option -k, --local-ip - added SHA-256 as a possible algorithm for digest authentication * version 0.9.6 - added new option -j to add a header - added support for multiple variable replacement (option -g) - added support for reading password from stdin - added support for SHA1 as digest algorithm for authentication - added supoprt for multiple test runs to calculate avarage timming values - fixed support for cygwin - fixed using wrong interface and symmetric signaling when running as root - fixed mem leak when using ares * version 0.9.5 - added support for TCP transport - added new option -E to specify the transport - renamed --invite-timeout to --timeout-after - added support for c-ares for DNS SRV lookups (http://daniel.haxx.se/projects/c-ares/) - improved lib ruli detection - openssl detection is only done when gnutls is missing - added username to flood mode - fixed millisecond sleep (thanks to Juri Glass) - fixed variable replacing (thanks to Todd Ingarfield) - fixed MD5 detection and compilation - re-enabled compilation and usage for CygWin * version 0.9.2 - added new option -D to specify maximum INVITE timeout value - option -c takes From header for MESSAGE's - added support for gnutls, which is preferred over openssl now see configure options --disable-gnutls and --with-libgnutls-prefix - fixed compilation for Solaris - fixed ACK building - fixed several issues with ports and usernames in ruri and outbound proxy - fixed mis-interpretation of ICMP error messages - fixed wrong port from SRV entry - fixed sipsk was still case-sensitive although strcasecmp was available * version 0.9.1 - added RFC compliant retransmissons of INVITE requests - fixed compilation errors for BSD and MacOSX - ACK creation will be rejected for strict routing - removed obsoleted options from configure * version 0.9.0 - added new option -S to allow symmetrical signaling in non-root mode - the option -z now takes an percent argument for the likeliness of binding removale - Via contains now branch with magic cookie - INVITE replies will be answered with ACKs automatically - basic support for Record-Route and loose routing (strict routing is not supported yet) - if an outbound proxy was given it will even be used after being redirected - request source and destiantion will be printed by -vvv - the read in file will be checked for conatining an empty line as body separator - fixed wrong checking for getopt header in configure - usage of global functions for memory allocation and integer conversion - separated the code into several small functions to make it easier understandable and better mantainable for the future * version 0.8.13 - nonce count contains the required leading zeros now - content length is printed as integer and not as hex value - DNS SRV lookup support through libruli (for target URI, outbound proxy and redirects) - all comparisons are now case in-sensitiv if suport by the system - destination port can be given in the outbound proxy argument * version 0.8.12 - Nagios compliant return codes (-N) - use strcasestr if available for case-insensitive compareings - ACK will be send for 40[17] replies on INVITE - just return the timing values of the operation (-A) - relaxed Contact check of option -C - numeric (-n) is now on by default * version 0.8.11 - try authentication with empty password instead of username if no password is given - support to read SIP message from standard input (-f -) - insert missing CR in front of LF (-L) - use different username for authentication (-u) - use multiple processes for usrloc, message and invite mode (-P) - search for a string in the reply with a regular expression (-q) * version 0.8.10 - send instant messages (just -M) - RFC 3261 compliant retransmission timer - added From (and To) tags - use MD5 from openssl if available - new argument -C to use any given contact value, even empty - support long options if available - support Nagios warn level with optional numbers of retransmissions - use a raw socket if available to support broken RFC 3581 implementations - small fixes and betaufications * version 0.8.9 - added Nagios compliant exit codes - fixed missing replies from un-symmetric servers - added outbound proxy option - fixed SIP issues (CSeq increasement on authorization and added missing To and From tags) * version 0.8.8 - added -p option to use an outbound proxy - added -C option to use given Contact in REGISTER - fixed authentication failure when password given as last option * version 0.8.7 - merged listening and sending socket into one - added rport parameter to all Via lines - improved error reporting on host name resolving problems * version 0.8.6 - fixed wrong number of retries and timeouts from configure - fixed missing username in default mode * version 0.8.5 - separated code into several files - introduced autoconf & automake - added option -o for sleeping between requests - added option -H to overwrite automatic hostname detection - ignores provisional responses in usrloc mode - added support for proxy authorization (407) - fixed missing port from uri in requests - number of retrys is configure option - maximum timeout value is configure option sipsak-0.9.8.1/Makefile.am000066400000000000000000000020521377576723000152350ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 CC = ${DISTCC} @CC@ bin_PROGRAMS = sipsak sipsak_SOURCES = src/transport.c src/transport.h src/auth.c src/auth.h src/header_f.c src/header_f.h src/helper.c src/helper.h src/md5.c src/md5.h src/md5global.h src/request.c src/request.h src/shoot.c src/shoot.h src/sipsak.c src/sipsak.h src/exit_code.h src/exit_code.c dist_man1_MANS=sipsak.1 # Tests TESTS = tests/check_md5 tests/check_auth tests/check_helper check_PROGRAMS = $(TESTS) tests_check_md5_SOURCES = tests/check_md5.c src/md5.h src/md5.c tests_check_md5_CFLAGS = @CHECK_CFLAGS@ -DRUNNING_CHECK tests_check_md5_LDADD = @CHECK_LIBS@ tests_check_auth_SOURCES = tests/check_auth.c src/auth.h src/auth.c src/sipsak.h src/md5.h src/md5.c src/exit_code.h src/helper.h src/helper.c tests_check_auth_CFLAGS = @CHECK_CFLAGS@ -DRUNNING_CHECK tests_check_auth_LDADD = @CHECK_LIBS@ tests_check_helper_SOURCES = tests/check_helper.c src/helper.h src/helper.c src/exit_code.h src/sipsak.h tests_check_helper_CFLAGS = @CHECK_CFLAGS@ -DRUNNING_CHECK tests_check_helper_LDADD = @CHECK_LIBS@ sipsak-0.9.8.1/NEWS000066400000000000000000000030571377576723000137060ustar00rootroot00000000000000* version 0.9.6 - added new option -j to add a header - added support for multiple variable replacement (option -g) - added support for SHA1 as digest algorithm for authentication * version 0.9.5 - renamed --invite-timeout to --timeout-factor - new option -E specifies the transport (UDP or TCP) - SRV lookup via c-ares is available (http://daniel.haxx.se/projects/c-ares/) * version 0.9.2 - prefers gnutls over openssl now - new option -D specifies the maximum INVITE timeout value - option -c specifies From header for MESSAGEs * version 0.9.1 - only fixes, no new options or features * version 0.9.0 - option -c in flood mode is replaced be -e * version 0.8.13 - SRV lookup is supported if libruli is available (http://www.nongnu.org/ruli/) * version 0.8.12 - numeric (-n) is now on by default * version 0.8.11 - try authentication with empty password instead of username if no password is given - support to read SIP message from standard input (-f -) * version 0.8.10 - send instant messages (just -M) - new argument -C to use any given contact value, even empty - support long options if available - support Nagios warn level with optional numbers of retransmissions * version 0.8.6 - important fixes * version 0.8.5 - ignores provisional responses in usrloc mode (-U) - added support for proxy authorization (407) - new argument -H overwrites automatic hostname detection - new argument -o introduces sleeping seconds before sending next request - number of retrys and maximum timeout values are configure options sipsak-0.9.8.1/README000066400000000000000000000016121377576723000140620ustar00rootroot00000000000000 REQUIREMENTS ============ None. Optional: - Have GnuTLS (http://www.gnutls.org/) or OpenSSL (http://www.openssl.org/) installed on your system to use their MD5 implementations instead of sipsak own version. - Have c-ares (http://daniel.haxx.se/projects/c-ares/) installed on your system to get DNS SRV lookup support compiled into your sipsak binary. INSTALLATION ============ The usual autoreconf --install ./configure make followed by an optional make install if the binary should be available for all users on that host. TESTS ===== Running tests can be done with make check be sure to have check -- unit test framework for C -- installed apt-get install check yum install check USAGE ===== Please read the man page. It also contains some typical usage examples at the end. Have fun Nils sipsak-0.9.8.1/TODO000066400000000000000000000011521377576723000136710ustar00rootroot00000000000000ToDo List (no order): - fix: wrong nonce value after aprox. 2000 users - fix: after aprox. 3 users the password is missing except -p was set - add displayname support to parse_uri - check the request target for the ACK - add option to tear down INVITED calls with BYE - add support for CANCEL - add support for spoofed IPs - parse the complete DNS resonses in util - increase hostname detection for the Via line - add support for priority and weigth in SRV responses - endless randtrash mode with logfile - support for IPv6 - strict RR support Feel free to send me patches for this (or other) features! sipsak-0.9.8.1/configure.ac000066400000000000000000000057771377576723000155100ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) AC_INIT([sipsak],[0.9.8.1],[github@ohlmeier.org]) AM_INIT_AUTOMAKE([subdir-objects]) AM_MAINTAINER_MODE AC_CONFIG_SRCDIR([src/sipsak.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC # Add -Wall if we are using GCC if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" fi SIPSAK_GCC_STACK_PROTECT_CC CHECK_PROG_DISTCC AC_PROG_INSTALL # Checks for libraries. AC_CANONICAL_HOST case "$host" in *-*-solaris*) LIBS="$LIBS -lposix4 -lsocket -lnsl" ;; *) LIBS="$LIBS" ;; esac AC_SUBST([LIBS]) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([ctype.h errno.h arpa/inet.h netdb.h netinet/in.h netinet/in_systm.h limits.h sys/poll.h regex.h signal.h stdarg.h stdlib.h stdio.h string.h sys/param.h sys/socket.h sys/time.h unistd.h sys/utsname.h],,[AC_MSG_ERROR([missing required header (see above)])],) AC_CHECK_HEADERS([getopt.h syslog.h]) AC_HEADER_SYS_WAIT AC_HEADER_TIME SIPSAK_IP_UDP SIPSAK_ICMP SIPSAK_RAW_SUPPORT # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_SELECT_ARGTYPES AC_CHECK_FUNCS([getchar gethostbyname gethostname getopt getpid gettimeofday memset ntohs regcomp select socket strchr strcmp strstr strtol uname],,[AC_MSG_ERROR([missing required function (see above)])]) AC_CHECK_FUNCS([calloc getdomainname getopt_long inet_ntop strncasecmp strcasestr syslog]) PKG_PROG_PKG_CONFIG # Check if the check unit test framework is available PKG_CHECK_MODULES([CHECK], [check >= 0.9.3], [ AC_DEFINE([HAVE_CHECK_H], [1], [Has check.h]) ],:) PKG_CHECK_MODULES([GNUTLS], [gnutls >= 1.0.0], [ AC_DEFINE([HAVE_GNUTLS], [1], [Has gnutls >= 1.0.0]) HAVE_GNUTLS=1 LIBS="${LIBS} ${GNUTLS_LIBS}" CFLAGS="${CFLAGS} ${GNUTLS_CFLAGS}" ], [ AC_CHECK_HEADERS([openssl/md5.h], [ AC_SEARCH_LIBS([MD5_Init], [crypto], [ AC_DEFINE([HAVE_CRYPTO_WITH_MD5], [1], [The crypto lib has MD5 functions]) ]) ]) AC_CHECK_HEADERS([openssl/sha.h], [ AC_SEARCH_LIBS([SHA1_Init], [crypto], [ AC_DEFINE([HAVE_CRYPTO_WITH_SHA1], [1], [The crpyto lib has SHA1 functions]) ]) ]) ]) PKG_CHECK_MODULES([GNUTLS319], [gnutls >= 3.1.9], [ AC_DEFINE([HAVE_GNUTLS_319], [1], [Has gntuls >= 3.1.9]) ],:) dnl --- dnl Check if GnuTLS has SRP support. dnl --- if test "$HAVE_GNUTLS" = "1"; then AC_CHECK_LIB(gnutls, gnutls_srp_verifier, [ AC_DEFINE(HAVE_GNUTLS_SRP, 1, [if you have the function gnutls_srp_verifier]) AC_SUBST(HAVE_GNUTLS_SRP, [1]) ]) fi PKG_CHECK_MODULES([CARES], [libcares], [ AC_DEFINE([HAVE_CARES_H], [1], [Has cares.h]) LIBS="$LIBS $CARES_LIBS" CFLAGS="$CFLAGS $CARES_CFLAGS" AC_CHECK_HEADERS([arpa/nameser.h]) ],:) # FIXME: this has to replaced with a real check # LIBS="$LIBS -lssl" SIPSAK_TIMER SIPSAK_OLD_FQDN SIPSAK_TLS SIPSAK_DBG_PRINT AC_CONFIG_FILES([Makefile]) AC_OUTPUT sipsak-0.9.8.1/indent.pro000066400000000000000000000012321377576723000152030ustar00rootroot00000000000000--blank-lines-after-declarations --blank-lines-after-procedures --blank-lines-before-block-comments --braces-on-func-def-line --braces-on-if-line --braces-on-struct-decl-line --break-after-boolean-operator --continue-at-parentheses --continuation-indentation8 --comment-line-length78 --declaration-indentation5 --dont-break-procedure-type --dont-cuddle-else --else-endif-column8 --format-first-column-comments --format-all-comments --indent-level4 --line-length78 --no-blank-lines-after-commas --no-space-after-function-call-names --parameter-indentation0 --preprocessor-indentation1 //--space-after-parentheses --swallow-optional-blank-lines --tab-size4 --use-tabs sipsak-0.9.8.1/m4/000077500000000000000000000000001377576723000135225ustar00rootroot00000000000000sipsak-0.9.8.1/m4/acinclude.m4000066400000000000000000000100701377576723000157110ustar00rootroot00000000000000AC_DEFUN([SIPSAK_IP_UDP], [ AC_CHECK_HEADERS([netinet/ip.h netinet/udp.h],,, [[#ifdef HAVE_NETINET_IN_SYSTM_H #include #include #include #endif ]]) ]) AC_DEFUN([SIPSAK_ICMP], [ AC_CHECK_HEADERS([netinet/ip_icmp.h],,, [[#ifdef HAVE_NETINET_IN_SYSTM_H #include #include #include #endif #ifdef HAVE_NETINET_IP_H #include #endif ]]) ]) AC_DEFUN([SIPSAK_RAW_SUPPORT], [ AC_REQUIRE([SIPSAK_IP_UDP]) AC_REQUIRE([SIPSAK_ICMP]) AC_CHECK_HEADERS([cygwin/icmp.h]) AC_ARG_ENABLE([raw-support], AS_HELP_STRING([--disable-raw-support], [compile without raw socket support]), [sipsak_raw_support=$enable_raw_support], [sipsak_raw_support=yes]) AC_MSG_CHECKING([raw socket support]) AS_IF([test "X$ac_cv_header_netinet_ip_h" = "Xno" || test "X$ac_cv_header_netinet_ip_icmp_h" = "Xno" || test "X$ac_cv_header_cygwin_icmp_h" = "Xyes"], [ sipsak_raw_support=no ]) AS_IF([test "X$sipsak_raw_support" = "Xyes"], [ AC_DEFINE([RAW_SUPPORT], [1], [Define to 1 to use raw socket support]) ]) AC_MSG_RESULT([$sipsak_raw_support]) ]) AC_DEFUN([SIPSAK_TIMER], [ # Check for T1 timer value def_timeout=500 AC_ARG_ENABLE([timeout],AS_HELP_STRING(--enable-timeout=SEC,SIP timer T1 in SEC milliseconds (default 500)),[def_timeout=$enableval]) if test "X$def_timeout" = "Xno"; then # no timeout makes no sense def_timeout=500 fi AC_DEFINE_UNQUOTED(DEFAULT_TIMEOUT, $def_timeout, [Default maximum timeout on waiting for response.]) ]) AC_DEFUN([SIPSAK_OLD_FQDN], [ AC_MSG_CHECKING([oldstyle numeric]) AC_ARG_ENABLE([ips], AS_HELP_STRING([--disbale-ips], [compile with oldstyle --numeric behavior]), [ AC_MSG_RESULT([yes]) AC_DEFINE([OLDSTYLE_FQDN], [1], [Oldstyle FQDN behavior]) ], [ AC_MSG_RESULT([not requested]) ]) ]) AC_DEFUN([SIPSAK_TLS], [ AC_MSG_CHECKING([disable TLS]) AC_ARG_ENABLE([tls], AS_HELP_STRING([--disable-tls], [compile without TLS transport]), [ AC_MSG_RESULT([yes]) AC_DEFINE([SIPSAK_NO_TLS], [1], [Skip TLS transport]) ], [ AC_MSG_RESULT([not requested]) ]) ]) AC_DEFUN([SIPSAK_DBG_PRINT], [ AC_MSG_CHECKING([enable debug messages]) AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [compile extra debug messages]), [ AC_MSG_RESULT([yes]) AC_DEFINE([SIPSAK_PRINT_DBG], [1], [Enable debug messages]) ], [ AC_MSG_RESULT([not requested]) ]) ]) AC_DEFUN([CHECK_PROG_DISTCC], [ AC_MSG_CHECKING([whether to use distcc]) AC_ARG_ENABLE([distcc], AS_HELP_STRING([--enable-distcc], [compile in parallel with distcc]), [ AC_MSG_RESULT([yes]) AC_CHECK_PROG([DISTCC], [distcc]) ], [ AC_MSG_RESULT([not requested]) ]) ]) dnl dnl Useful macros for autoconf to check for ssp-patched gcc dnl 1.0 - September 2003 - Tiago Sousa dnl dnl About ssp: dnl GCC extension for protecting applications from stack-smashing attacks dnl http://www.research.ibm.com/trl/projects/security/ssp/ dnl dnl Usage: dnl After calling the correct AC_LANG_*, use the corresponding macro: dnl dnl GCC_STACK_PROTECT_CC dnl checks -fstack-protector with the C compiler, if it exists then updates dnl CFLAGS and defines ENABLE_SSP_CC dnl dnl GCC_STACK_PROTECT_CXX dnl checks -fstack-protector with the C++ compiler, if it exists then updates dnl CXXFLAGS and defines ENABLE_SSP_CXX dnl AC_DEFUN([SIPSAK_GCC_STACK_PROTECT_CC],[ ssp_cc=yes if test "X$CC" != "X"; then AC_MSG_CHECKING([whether ${CC} accepts -fstack-protector]) ssp_old_cflags="$CFLAGS" CFLAGS="$CFLAGS -fstack-protector" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [], [], [ssp_cc=no]) AC_MSG_RESULT([$ssp_cc]) if test "X$ssp_cc" = "Xno"; then CFLAGS="$ssp_old_cflags" else AC_DEFINE([ENABLE_SSP_CC], 1, [Define if SSP C support is enabled.]) fi fi ]) dnl *-*wedit:notab*-* Please keep this as the last line. sipsak-0.9.8.1/sipsak.1000066400000000000000000000444571377576723000145740ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii sipsak.1 .\" .TH SIPSAK 1 "JULY 2002 - SEPTEMBER 2005" Linux "User Manuals" .SH NAME sipsak \- a utility for various tests on sip servers and user agents .SH SYNOPSIS .B sipsak [-dFGhiILnNMRSTUVvwz] [-a .I PASSWORD .B ] [-b .I NUMBER .B ] [-c .I SIPURI .B ] [-C .I SIPURI .B ] [-D .I NUMBER .B ] [-e .I NUMBER .B ] [-E .I STRING .B ] [-f .I FILE .B ] [-g .I STRING .B ] [-H .I HOSTNAME .B ] [-j .I STRING .B ] [-J .I STRING .B ] [-l .I PORT .B ] [-m .I NUMBER .B ] [-o .I NUMBER .B ] [-p .I HOSTNAME .B ] [-P .I NUMBER .B ] [-q .I REGEXP .B ] [-r .I PORT .B ] [-t .I NUMBER .B ] [-u .I STRING .B ] [-W .I NUMBER .B ] [-x .I NUMBER .B ] -s .I SIPURI .SH DESCRIPTION .B sipsak is a SIP stress and diagnostics utility. It sends SIP requests to the server within the .BR sip-uri and examines received responses. It runs in one of the following modes: .IP "- default mode" A SIP message is sent to destination in .BR sip-uri and reply status is displayed. The request is either taken from .BR filename or generated as a new OPTIONS message. .IP "- traceroute mode (-T)" This mode is useful for learning request's path. It operates similarly to IP-layer utility .BR traceroute (8). .IP "- message mode (-M)" Sends a short message (similar to SMS from the mobile phones) to a given target. With the option .BR -B the content of the MESSAGE can be set. Useful might be the options .BR -c and .BR -O in this mode. .IP "- usrloc mode (-U)" Stress mode for SIP registrar. .B sipsak keeps registering to a SIP server at high pace. Additionally the registrar can be stressed with the .BR -I or the .BR -M option. If .BR -I and .BR -M are omitted .B sipsak can be used to register any given contact (with the .BR -C option) for an account at a registrar and to query the current bindings for an account at a registrar. .IP "- randtrash mode (-R)" Parser torture mode. .B sipsak keeps sending randomly corrupted messages to torture a SIP server's parser. .IP "- flood mode (-F)" Stress mode for SIP servers. .B sipsak keeps sending requests to a SIP server at high pace. .PP If c-ares (http://daniel.haxx.se/projects/c-ares/) support is compiled into the .B sipsak binary, then first a SRV lookup for _sip._tcp.hostname is made. If that fails a SRV lookup for _sip._udp.hostname is made. And if this lookup fails a normal A lookup is made. If a port was given in the target URI the SRV lookup is omitted. Failover, load distribution and other transports are not supported yet. .SH OPTIONS .IP "-a, --password PASSWORD" With the given .I PASSWORD an authentication will be tried on received '401 Unauthorized'. Authorization will be tried on time. If this option is omitted an authorization with an empty password ("") will be tried. If the password is equal to .I - the password will be read from the standard input (e.g. the keyboard). This prevents other users on the same host from seeing the password in the process list. .B NOTE: the password still can be read from the memory if other users have access to it. .IP "-A, --timing" prints only the timing values of the test run if verbosity is zero because no .BR -v was given. If one or more .BR -v were given this option will be ignored. .IP "-b, --appendix-begin NUMBER" The starting number which is appended to the user name in the usrloc mode. This .I NUMBER is increased until it reaches the value given by the .BR -e parameter. If omitted the starting number will be one. .IP "-B, --message-body STRING" The given .I STRING will be used as the body for outgoing MESSAGE requests. .IP "-c, --from SIPURI" The given .I SIPURI will be used in the From header if .B sipsak runs in the message mode (initiated with the .BR -M option). This is helpful to present the receiver of a MESSAGE a meaningful and usable address to where maybe even responses can be sent. .IP "-C, --contact SIPURI" This is the content of the Contact header in the usrloc mode. This allows to insert forwards like for mail. For example you can insert the uri of your first SIP account at a second account, thus all calls to the second account will be forwarded to the first account. As the argument to this option will not be enclosed in brackets you can give also multiple contacts in the raw format as comma separated list. The special words .B empty or .B none will result in no contact header in the REGISTER request and thus the server should answer with the current bindings for the account at the registrar. The special words .B * or .B star will result in Contact header containing just a star, e.g. to remove all bindings by using expires value 0 together with this Contact. .IP "-d, --ignore-redirects" If this option is set all redirects will be ignored. By default without this option received redirects will be respected. This option is automatically activated in the randtrash mode and in the flood mode. .IP "-D, --timeout-factor NUMBER" The SIP_T1 timer is getting multiplied with the given NUMBER. After receiving a provisional response for an INVITE request, or when a reliable transport like TCP or TLS is used .B sipsak waits for the resulting amount of time for a final response until it gives up. .IP "-e, --appendix-end NUMBER" The ending number which is appended to the user name in the usrloc mode. This number is increased until it reaches this ending .I number. In the flood mode this is the maximum number of messages which will be sent. If omitted the default value is 2^31 (2147483647) in the flood mode. .IP "-E, --transport STRING" The value of .I STRING will be used as IP transport for sending and receiving requests and responses. This option overwrites any result from the URI evaluation and SRV lookup. Currently only 'udp' and 'tcp' are accepted as value for .I STRING. .IP "-f, --filename FILE" The content of .I FILE will be read in in binary mode and will be used as replacement for the alternatively created sip message. This can used in the default mode to make other requests than OPTIONS requests (e.g. INVITE). By default missing carriage returns in front of line feeds will be inserted (use .BR -L to de-activate this function). If the filename is equal to .I - the file is read from standard input, e.g. from the keyboard or a pipe. Please note that the manipulation functions (e.g. inserting Via header) are only tested with RFC conform requests. Additionally special strings within the file can be replaced with some local or given values (see .BR -g and .BR -G for details). .IP "-F, --flood-mode" This option activates the flood mode. In this mode OPTIONS requests with increasing CSeq numbers are sent to the server. Replies are ignored -- source port 9 (discard) of localhost is advertised in topmost Via. .IP "-h, --help" Prints out a simple usage help message. If the long option .BR --help is available it will print out a help message with the available long options. .IP "-g, --replace-string STRING" Activates the replacement of $replace$ within the request (usually read in from a file) with the .I STRING. Alternatively you can also specify a list of attributes and values. This list has to start and end with a non alpha-numeric character. The same character has to be used also as separator between the attribute and the value and between new further attribute value pairs. The string "$attribute$" will be replaced with the value string in the message. .IP "-G, --replace" Activates the automatic replacement of the following variables in the request (usually read in from a file): .B $dsthost$ will be replaced with the host or domainname which is given by the .B -s parameter. .B $srchost$ will be replaced by the hostname of the local machine. .B $port$ will be replaced by the local listening port of .B sipsak. .B $user$ will be replaced by the username which is given by the .B -s parameter. .IP "-H, --hostname HOSTNAME" Overwrites the automatic detection of the hostname with the given parameter. .B Warning: use this with caution (preferable only if the automatic detection fails). .IP "-i, --no-via" Deactivates the insertion of the Via line of the localhost. .B Warning: this probably disables the receiving of the responses from the server. .IP "-I, --invite-mode" Activates the Invites cycles within the usrloc mode. It should be combined with .BR -U. In this combination .B sipsak first registers a user, and then simulates an invitation to this user. First an Invite is sent, this is replied with 200 OK and finally an ACK is sent. This option can also be used without .BR -U , but you should be sure to NOT invite real UAs with this option. In the case of a missing .BR -U the .BR "-l PORT" is required because only if you made a .BR -U run with a fixed local port before, a run with .BR -I and the same fixed local port can be successful. .B Warning: sipsak is no real UA and invitations to real UAs can result in unexpected behaviour. .IP "-j, --headers STRING" The .BR string will be added as one or more additional headers to the request. The string "\\n" (note: two characters) will be replaced with CRLF and thus result in two separate headers. That way more then one header can be added. .IP "-J, --autohash STRING" The .BR string will be used as the H(A1) input to the digest authentication response calculation. Thus no password from the .BR -a option is required if this option is provided. The given .BR string is expected to be a hex string with the length of the used hash function. .IP "-k, --local-ip STRING" The local ip address to be used .IP "-l, --local-port PORT" The receiving UDP socket will use the local network .I port. Useful if a file is given by .BR -f which contains a correct Via line. Check the .BR -S option for details how sipsak sends and receives messages. .IP "-L, --no-crlf" De-activates the insertion of carriage returns (\\r) before all line feeds (\\n) (which is not already proceeded by carriage return) if the input is coming from a file ( .BR -f ). Without this option also an empty line will be appended to the request if required. .IP "-m, --max-forwards NUMBER" This sets the value of the Max-Forward header field. If omitted no Max-Forward field will be inserted. If omitted in the traceroute mode .BR number will be 255. .IP "-M, --message-mode" This activates the Messages cycles within the usrloc mode (known from .B sipsak versions pre 0.8.0 within the normal usrloc test). This option should be combined with .BR -U so that a successful registration will be tested with a test message to the user and replied with 200 OK. But this option can also be used without the .BR -U option. .B Warning: using without .BR -U can cause unexpected behavior. .IP "-n, --numeric" Instead of the full qualified domain name in the Via line the IP of the local host will be used. This option is now on by default. .IP "-N, --nagios-code" Use Nagios compliant return codes instead of the normal sipsak ones. This means .B sipsak will return 0 if everything was ok and 2 in case of any error (local or remote). .IP "-o, --sleep NUMBER" .B sipsak will sleep for .BR NUMBER ms before it starts the next cycle in the usrloc mode. This will slow down the whole test process to be more realistic. Each cycle will be still completed as fast as possible, but the whole test will be slowed down. .IP "-O, --disposition STRING" The given .BR STRING will be used as the content for the Content-Disposition header. Without this option there will be no Content-Disposition header in the request. .IP "-p, --outbound-proxy HOSTNAME[:PORT]" the address of the hostname is the target where the request will be sent to (outgoing proxy). Use this if the destination host is different from the host part of the request uri. The hostname is resolved via DNS SRV if supported (see description for SRV resolving) and no port is given. .IP "-P, --processes NUMBER" Start .BR NUMBER of processes in parallel to do the send and reply checking. Only makes sense if a higher number for .BR -e is given in the usrloc, message or invite mode. .IP "-q, --search REGEXP" match replies against .BR REGEXP and return false if no match occurred. Useful for example to detect server name in Server header field. .IP "-r, --remote-port PORT" Instead of the default sip port 5060 the .BR PORT will be used. Alternatively the remote port can be given within the sip uri of the .BR -s parameter. .IP "-R, --random-mode" This activates the randtrash mode. In this mode OPTIONS requests will be sent to server with increasing numbers of randomly crashed characters within this request. The position within the request and the replacing character are randomly chosen. Any other response than Bad request (4xx) will stop this mode. Also three unresponded sends will stop this mode. With the .BR -t parameter the maximum of trashed characters can be given. .IP "-s, --sip-uri SIPURI" This mandatory option sets the destination of the request. It depends on the mode if only the server name or also a user name is mandatory. Example for a full .BR SIPURI : .I sip:test@foo.bar:123 See the note in the description part about SRV lookups for details how the hostname of this URI is converted into an IP and port. .IP "-S, --symmetric" With this option .B sipsak will use only one port for sending and receiving messages. With this option the local port for sending will be the value from the .BR -l option. In the default mode .B sipsak sends from a random port and listens on the given port from the .BR -l option. .B Note: With this option .B sipsak will not be able to receive replies from servers with asymmetric signaling (and broken rport implementation) like the Cisco proxy. If you run .B sipsak as root and with raw socket support (check the output from the .BR -V option) then this option is not required because in this case .B sipsak already uses only one port for sending and receiving messages. .IP "-t, --trash-chars NUMBER" This parameter specifies the maximum of trashed characters in the randtrash mode. If omitted .BR NUMBER will be set to the length of the request. .IP "-T, --traceroute-mode" This activates the traceroute mode. This mode works like the well known .BR traceroute(8) command expect that not the number of network hops is counted rather the number of servers on the way to the destination user. Also the round trip time of each request is printed out, but due to a limitation within the sip protocol the identity (IP or name) can only be determined and printed out if the response from the server contains a warning header field. In this mode on each outgoing request the value of the Max-Forwards header field is increased, starting with one. The maximum of the Max-Forwards header will be 255 if no other value is given by the .BR -m parameter. Any other response than 483 or 1xx is treated as a final response and will terminate this mode. .IP "-u, --auth-username STRING" Use the given .BR STRING as username value for the authentication (different account and authentication username). .IP "-U, --usrloc-mode" This activates the usrloc mode. Without the .BR -I or the .BR -M option, this only registers users at a registrar. With one of the above options the previous registered user will also be probed, wether with a simulated call flow (invite, 200, ack) or with an instant message (message, 200). One password for all users accounts within the usrloc test can be given with the .BR -a option. A user name is mandatory for this mode in the .BR -s parameter. The number starting from the .BR -b parameter to the .BR -e parameter is appended the user name. If the .BR -b and the .BR -e parameter are omitted, only one run with the given username, but without append number to the usernames is done. .IP "-v, --verbose" This parameter increases the output verbosity. No .BR -v means nearly no output except in traceroute and error messages. The maximum of three v's prints out the content of all packets received and sent. .IP "-V, --version" Prints out the name and version number of .B sipsak and the options which were compiled into the binary. .IP "-w, --extract-ip" Activates the extraction of the IP or hostname from the Warning header field. .IP "-W, --nagios-warn NUMBER" Return Nagios warn exit code (1) if the number of retransmissions before success was above the given number. .IP "-x, --expires NUMBER" Sets the value of the Expires header to the given number. .IP "-z, --remove-bindings" Activates the randomly removing of old bindings in the usrloc mode. How many percent of the bindings will be removed, is determined by the USRLOC_REMOVE_PERCENT define within the code (set it before compilation). Multiple removing of bindings is possible, and cannot be prevented. .IP "-Z, --timer-t1" Sets the amount of milliseconds for the SIP timer T1. It determines the length of the gaps between two retransmissions of a request on a unreliable transport. Default value is 500 if not changed via the configure option --enable-timeout. .SH RETURN VALUES The return value 0 means that a 200 was received. 1 means something else than 1xx or 2xx was received. 2 will be returned on local errors like non resolvable names or wrong options combination. 3 will be returned on remote errors like socket errors (e.g. icmp error), redirects without a contact header or simply no answer (timeout). If the .BR -N option was given the return code will be 2 in case of any (local or remote) error. 1 in case there have been retransmissions from .B sipsak to the server. And 0 if there was no error at all. .SH CAUTION Use .B sipsak responsibly. Running it in any of the stress modes puts substantial burden on network and server under test. .SH EXAMPLES .IP "sipsak -vv -s sip:nobody@foo.bar" displays received replies. .IP "sipsak -T -s sip:nobody@foo.bar" traces SIP path to nobody. .IP "sipsak -U -C sip:me@home -x 3600 -a password -s sip:myself@company" inserts forwarding from work to home for one hour. .IP "sipsak -f bye.sip -g '!FTAG!345.af23!TTAG!1208.12!' -s sip:myproxy" reads the file bye.sip, replaces $FTAG$ with 345.af23 and $TTAG$ with 1208.12 and finally send this message to myproxy .SH LIMITATIONS / NOT IMPLEMENTED Many servers may decide NOT to include SIP "Warning" header fields. Unfortunately, this makes displaying IP addresses of SIP servers in traceroute mode impossible. IPv6 is not supported. Missing support for the Record-Route and Route header. .SH BUGS sipsak is only tested against the SIP Express Router (ser) though their could be various bugs. Please feel free to mail them to the author. .SH AUTHOR Nils Ohlmeier .SH "SEE ALSO" .BR traceroute (8) sipsak-0.9.8.1/src/000077500000000000000000000000001377576723000137715ustar00rootroot00000000000000sipsak-0.9.8.1/src/auth.c000066400000000000000000000350131377576723000151000ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #include "auth.h" #include "exit_code.h" #include "helper.h" #include "md5.h" #ifdef HAVE_OPENSSL_SHA1 # include #endif #define SIPSAK_ALGO_MD5 1 #define SIPSAK_ALGO_SHA1 2 #define SIPSAK_ALGO_SHA256 3 unsigned int nonce_count; /* converts a hash into hex output taken from the RFC 2617 */ void cvt_hex(unsigned char *_b, unsigned char *_h, int length) { unsigned short i; for (i = 0; i < length; i++) { unsigned char j; j = (_b[i] >> 4) & 0xf; if (j <= (unsigned char)9) { _h[i * 2] = (j + (unsigned char)'0'); } else { _h[i * 2] = (unsigned char)(j + (unsigned char)'a' - (unsigned char)10); } j = _b[i] & 0xf; if (j <= (unsigned char)9) { _h[i * 2 + 1] = (j + (unsigned char)'0'); } else { _h[i * 2 + 1] = (unsigned char)(j + (unsigned char)'a' - (unsigned char)10); } }; _h[2*length] = '\0'; } /* check for, create and insert a auth header into the message */ void insert_auth(char *message, char *authreq, char *username, char *password, char *auth_username, char *authhash, int namebeg, int nameend) { char *auth, *begin, *end, *insert, *backup, *realm, *usern, *nonce; char *method, *uri; char *qop_tmp = NULL; unsigned char ha1[SIPSAK_HASHLEN], ha2[SIPSAK_HASHLEN], resp[SIPSAK_HASHLEN]; unsigned char ha1_hex[SIPSAK_HASHHEXLEN+1], ha2_hex[SIPSAK_HASHHEXLEN+1], resp_hex[SIPSAK_HASHHEXLEN+1]; int qop_auth=0, proxy_auth=0, algo=0; MD5_CTX Md5Ctx; #ifdef HAVE_OPENSSL_SHA1 SHA_CTX Sha1Ctx; SHA256_CTX Sha256Ctx; #endif auth=begin=end=insert=backup=realm=usern=nonce=method=uri = NULL; memset(&ha1[0], '\0', SIPSAK_HASHLEN); memset(&ha2[0], '\0', SIPSAK_HASHLEN); memset(&resp[0], '\0', SIPSAK_HASHLEN); memset(&ha1_hex[0], '\0', SIPSAK_HASHHEXLEN+1); memset(&ha2_hex[0], '\0', SIPSAK_HASHHEXLEN+1); memset(&resp_hex[0], '\0', SIPSAK_HASHHEXLEN+1); /* prevent double auth insertion */ if ((begin=STRCASESTR(message, AUTH_STR))!=NULL || (begin=STRCASESTR(message, PROXYAUZ_STR))!=NULL) { fprintf(stderr, "request:\n%s\nresponse:\n%s\nerror: authorization failed\n " " request already contains (Proxy-) Authorization, but " "received 40[1|7], see above\n", message, authreq); exit_code(2, __PRETTY_FUNCTION__, "failed to add auth header, because request contained already one"); } /* make a backup of all except the request line because for simplicity we insert the auth header direct behind the request line */ insert=strchr(message, '\n'); if (!insert) { fprintf(stderr, "%s\nerror: failed to locate new line in request message\n", message); exit_code(3, __PRETTY_FUNCTION__, "missing new line in request"); } insert++; backup=str_alloc(strlen(insert)+1); strncpy(backup, insert, strlen(insert)); begin=STRCASESTR(authreq, WWWAUTH_STR); if (begin==NULL) { begin=STRCASESTR(authreq, PROXYAUTH_STR); proxy_auth = 1; } if (begin) { /* make a copy of the auth header to prevent that our searches hit content of other header fields */ end=strchr(begin, '\n'); if (end == NULL) { fprintf(stderr, "%s\nerror: failed to locate new line after auth header\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "missing new line after auth header"); } auth=str_alloc((size_t)(end-begin+1)); strncpy(auth, begin, (size_t)(end-begin)); /* we support Digest with MD5 or SHA1 */ if ((begin=STRCASESTR(auth, "Basic"))!=NULL) { fprintf(stderr, "%s\nerror: authentication method Basic is deprecated since" " RFC 3261 and not supported by sipsak\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "authentication method 'Basic' is deprecated"); } if ((begin=STRCASESTR(auth, "Digest"))==NULL) { fprintf(stderr, "%s\nerror: couldn't find authentication method Digest in " "the 40[1|7] response above\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "missing authentication method 'Digest' in reply"); } if ((begin=STRCASESTR(auth, "algorithm="))!=NULL) { begin+=10; if ((STRNCASECMP(begin, "MD5", 3))==0 || (STRNCASECMP(begin, "\"MD5\"", 5))==0) { algo = SIPSAK_ALGO_MD5; } #ifdef HAVE_OPENSSL_SHA1 else if ((STRNCASECMP(begin, "SHA1", 4))==0 || (STRNCASECMP(begin, "\"SHA1\"", 6))==0) { algo = SIPSAK_ALGO_SHA1; } else if ((STRNCASECMP(begin, "SHA-256", 7))==0 || (STRNCASECMP(begin, "\"SHA-256\"", 9))==0) { algo = SIPSAK_ALGO_SHA256; } #endif else { fprintf(stderr, "\n%s\nerror: unsupported authentication algorithm\n", authreq); exit_code(2, __PRETTY_FUNCTION__, "unsupported authentication algorithm"); } } else { algo = SIPSAK_ALGO_MD5; } /* we need the username at some points */ if (auth_username != NULL) { usern = auth_username; } else { usern=str_alloc(strlen(username)+11); if (usern == NULL) { fprintf(stderr, "error: failed to allocate space for username: %s\n", username); exit_code(3, __PRETTY_FUNCTION__, "memory allocation failure"); } if (nameend>0) snprintf(usern, strlen(username)+10, "%s%d", username, namebeg); else snprintf(usern, strlen(username)+1, "%s", username); } /* extract the method from the original request */ end=strchr(message, ' '); if (end == NULL) { fprintf(stderr, "%s\nerror: failed to locate space in message first line\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "missing space in message"); } method=str_alloc((size_t)(end-message+1)); strncpy(method, message, (size_t)(end-message)); /* extract the uri also */ begin=end++; begin++; end=strchr(end, ' '); if (end == NULL) { fprintf(stderr, "%s\nerror: failed to locate space in message first line\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "missing space in message"); } uri=str_alloc((size_t)(end-begin+1)); strncpy(uri, begin, (size_t)(end-begin)); /* lets start with some basic stuff... username, uri and algorithm */ if (proxy_auth == 1) { snprintf(insert, PROXYAUZ_STR_LEN+1, PROXYAUZ_STR); insert+=PROXYAUZ_STR_LEN; } else { snprintf(insert, AUTH_STR_LEN+1, AUTH_STR); insert+=AUTH_STR_LEN; } snprintf(insert, strlen(usern)+14, "username=\"%s\", ", usern); insert+=strlen(insert); snprintf(insert, strlen(uri)+9, "uri=\"%s\", ", uri); insert+=strlen(insert); snprintf(insert, ALGO_STR_LEN+1, ALGO_STR); insert+=ALGO_STR_LEN; if (algo == SIPSAK_ALGO_MD5) { snprintf(insert, MD5_STR_LEN+1, MD5_STR); insert+=MD5_STR_LEN; } #ifdef HAVE_OPENSSL_SHA1 else if (algo == SIPSAK_ALGO_SHA1) { snprintf(insert, SHA1_STR_LEN+1, SHA1_STR); insert+=SHA1_STR_LEN; } else if (algo == SIPSAK_ALGO_SHA256) { snprintf(insert, SHA256_STR_LEN+1, SHA256_STR); insert+=SHA256_STR_LEN; } #endif /* search for the realm, copy it to request and extract it for hash*/ if ((begin=STRCASESTR(auth, REALM_STR))!=NULL) { end=strchr(begin, ','); if (!end) end=strchr(begin, '\r'); strncpy(insert, begin, (size_t)(end-begin+1)); insert=insert+(end-begin+1); if (*(insert-1) == '\r') *(insert-1)=','; snprintf(insert, 2, " "); insert++; begin+=REALM_STR_LEN+1; end--; realm=str_alloc((size_t)(end-begin+1)); strncpy(realm, begin, (size_t)(end-begin)); } else { fprintf(stderr, "%s\nerror: realm not found in 401 above\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "realm not found in reply"); } /* copy opaque if needed */ if ((begin=STRCASESTR(auth, OPAQUE_STR))!=NULL) { end=strchr(begin, ','); if (!end) { end=strchr(begin, '\r'); } strncpy(insert, begin, (size_t)(end-begin+1)); insert=insert+(end-begin+1); if (*(insert-1) == '\r') *(insert-1)=','; snprintf(insert, 2, " "); insert++; } /* lets see if qop=auth is uspported */ if ((begin=STRCASESTR(auth, QOP_STR))!=NULL) { if (STRCASESTR(begin, QOPAUTH_STR)==NULL) { fprintf(stderr, "response\n%s\nerror: qop \"auth\" not supported by" " server\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "qop 'auth' is not supported by server"); } qop_auth=1; } /* search, copy and extract the nonce */ if ((begin=STRCASESTR(auth, NONCE_STR))!=NULL) { end=strchr(begin, ','); if (!end) end=strchr(begin, '\r'); strncpy(insert, begin, (size_t)(end-begin+1)); insert=insert+(end-begin+1); if (*(insert-1) == '\r') *(insert-1)=','; snprintf(insert, 2, " "); insert++; begin+=NONCE_STR_LEN+1; end--; nonce=str_alloc((size_t)(end-begin+1)); strncpy(nonce, begin, (size_t)(end-begin)); } else { fprintf(stderr, "%s\nerror: nonce not found in 401 above\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "missing nonce in reply"); } /* if qop is supported we need som additional header */ if (qop_auth == 1) { unsigned int cnonce; snprintf(insert, QOP_STR_LEN+QOPAUTH_STR_LEN+3, "%s%s, ", QOP_STR, QOPAUTH_STR); insert+=strlen(insert); nonce_count++; snprintf(insert, NC_STR_LEN+11, "%s%08x, ", NC_STR, nonce_count); insert+=strlen(insert); cnonce=(unsigned int)rand(); snprintf(insert, 12+8, "cnonce=\"%x\", ", cnonce); insert+=strlen(insert); /* hopefully 100 is enough */ qop_tmp=str_alloc(100); snprintf(qop_tmp, 8+8+8, "%08x:%x:auth:", nonce_count, cnonce); } /* if no password is given we try it with empty password */ if (!password) password = EMPTY_STR; if (algo == SIPSAK_ALGO_MD5) { if (authhash) { strncpy((char*)&ha1_hex[0], authhash, SIPSAK_HASHHEXLEN_MD5); } else { MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, usern, (unsigned int)strlen(usern)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, realm, (unsigned int)strlen(realm)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, password, (unsigned int)strlen(password)); MD5Final(&ha1[0], &Md5Ctx); cvt_hex(&ha1[0], &ha1_hex[0], SIPSAK_HASHLEN_MD5); } MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, method, (unsigned int)strlen(method)); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, uri, (unsigned int)strlen(uri)); MD5Final(&ha2[0], &Md5Ctx); cvt_hex(&ha2[0], &ha2_hex[0], SIPSAK_HASHLEN_MD5); MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, &ha1_hex, SIPSAK_HASHHEXLEN_MD5); MD5Update(&Md5Ctx, ":", 1); MD5Update(&Md5Ctx, nonce, (unsigned int)strlen(nonce)); MD5Update(&Md5Ctx, ":", 1); if (qop_auth == 1) { MD5Update(&Md5Ctx, qop_tmp, (unsigned int)strlen(qop_tmp)); } MD5Update(&Md5Ctx, &ha2_hex, SIPSAK_HASHHEXLEN_MD5); MD5Final(&resp[0], &Md5Ctx); cvt_hex(&resp[0], &resp_hex[0], SIPSAK_HASHLEN_MD5); } #ifdef HAVE_OPENSSL_SHA1 else if (algo == SIPSAK_ALGO_SHA1) { if (authhash) { strncpy((char*)&ha1_hex[0], authhash, SIPSAK_HASHHEXLEN_SHA1); } else { SHA1_Init(&Sha1Ctx); SHA1_Update(&Sha1Ctx, usern, (unsigned int)strlen(usern)); SHA1_Update(&Sha1Ctx, ":", 1); SHA1_Update(&Sha1Ctx, realm, (unsigned int)strlen(realm)); SHA1_Update(&Sha1Ctx, ":", 1); SHA1_Update(&Sha1Ctx, password, (unsigned int)strlen(password)); SHA1_Final(&ha1[0], &Sha1Ctx); cvt_hex(&ha1[0], &ha1_hex[0], SIPSAK_HASHLEN_SHA1); } SHA1_Init(&Sha1Ctx); SHA1_Update(&Sha1Ctx, method, (unsigned int)strlen(method)); SHA1_Update(&Sha1Ctx, ":", 1); SHA1_Update(&Sha1Ctx, uri, (unsigned int)strlen(uri)); SHA1_Final(&ha2[0], &Sha1Ctx); cvt_hex(&ha2[0], &ha2_hex[0], SIPSAK_HASHLEN_SHA1); SHA1_Init(&Sha1Ctx); SHA1_Update(&Sha1Ctx, &ha1_hex, SIPSAK_HASHHEXLEN_SHA1); SHA1_Update(&Sha1Ctx, ":", 1); SHA1_Update(&Sha1Ctx, nonce, (unsigned int)strlen(nonce)); SHA1_Update(&Sha1Ctx, ":", 1); if (qop_auth == 1) { SHA1_Update(&Sha1Ctx, qop_tmp, (unsigned int)strlen(qop_tmp)); } SHA1_Update(&Sha1Ctx, &ha2_hex, SIPSAK_HASHHEXLEN_SHA1); SHA1_Final(&resp[0], &Sha1Ctx); cvt_hex(&resp[0], &resp_hex[0], SIPSAK_HASHLEN_SHA1); } else if (algo == SIPSAK_ALGO_SHA256) { if (authhash) { strncpy((char*)&ha1_hex[0], authhash, SIPSAK_HASHHEXLEN_SHA256); } else { SHA256_Init(&Sha256Ctx); SHA256_Update(&Sha256Ctx, usern, (unsigned int)strlen(usern)); SHA256_Update(&Sha256Ctx, ":", 1); SHA256_Update(&Sha256Ctx, realm, (unsigned int)strlen(realm)); SHA256_Update(&Sha256Ctx, ":", 1); SHA256_Update(&Sha256Ctx, password, (unsigned int)strlen(password)); SHA256_Final(&ha1[0], &Sha256Ctx); cvt_hex(&ha1[0], &ha1_hex[0], SIPSAK_HASHLEN_SHA256); } SHA256_Init(&Sha256Ctx); SHA256_Update(&Sha256Ctx, method, (unsigned int)strlen(method)); SHA256_Update(&Sha256Ctx, ":", 1); SHA256_Update(&Sha256Ctx, uri, (unsigned int)strlen(uri)); SHA256_Final(&ha2[0], &Sha256Ctx); cvt_hex(&ha2[0], &ha2_hex[0], SIPSAK_HASHLEN_SHA256); SHA256_Init(&Sha256Ctx); SHA256_Update(&Sha256Ctx, &ha1_hex, SIPSAK_HASHHEXLEN_SHA256); SHA256_Update(&Sha256Ctx, ":", 1); SHA256_Update(&Sha256Ctx, nonce, (unsigned int)strlen(nonce)); SHA256_Update(&Sha256Ctx, ":", 1); if (qop_auth == 1) { SHA256_Update(&Sha256Ctx, qop_tmp, (unsigned int)strlen(qop_tmp)); } SHA256_Update(&Sha256Ctx, &ha2_hex, SIPSAK_HASHHEXLEN_SHA256); SHA256_Final(&resp[0], &Sha256Ctx); cvt_hex(&resp[0], &resp_hex[0], SIPSAK_HASHLEN_SHA256); } #endif snprintf(insert, RESPONSE_STR_LEN+1, RESPONSE_STR); insert+=RESPONSE_STR_LEN; snprintf(insert, sizeof(resp_hex) + 8,"\"%s\"\r\n", &resp_hex[0]); insert+=strlen(insert); /* the auth header is complete, reinsert the rest of the request */ strncpy(insert, backup, strlen(backup)); } else { fprintf(stderr, "%s\nerror: couldn't find Proxy- or WWW-Authentication header" " in the 401 response above\n", authreq); exit_code(3, __PRETTY_FUNCTION__, "missing authentication header in reply"); } if (verbose>1) printf("authorizing\n"); /* hopefully we free all here */ free(backup); free(auth); free(method); free(uri); free(realm); free(nonce); if (auth_username == NULL) free(usern); if (qop_auth == 1) free(qop_tmp); } sipsak-0.9.8.1/src/auth.h000066400000000000000000000015541377576723000151100ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_AUTH_H #define SIPSAK_AUTH_H extern unsigned int nonce_count; void insert_auth(char *message, char *authreq, char *username, char *password, char *auth_username, char *authhash, int namebeg, int nameend); #endif sipsak-0.9.8.1/src/exit_code.c000066400000000000000000000032601377576723000161010ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #if HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_SYSLOG_H # include #endif #include "exit_code.h" #include "transport.h" enum exit_modes exit_mode = EM_DEFAULT; int sysl; void log_message(const char *message) { if ((sysl > 3) && (message != NULL)) { #ifdef HAVE_SYSLOG syslog(LOG_INFO, "%s", message); #endif } } void exit_code(int code, const char *function, const char *reason) { shutdown_network(); if ((sysl > 0) && (reason != NULL)) { #ifdef HAVE_SYSLOG syslog(LOG_INFO, "%s: %s", function, reason); closelog(); #endif } switch(exit_mode) { case EM_DEFAULT: if (code == 4) { exit(0); } else { exit(code); } case EM_NAGIOS: if (code == 0) { printf("SIP ok\n"); exit(0); } else if (code == 4) { printf("SIP warning\n"); exit(1); } else { printf("SIP failure\n"); exit(2); } default: fprintf(stderr, "ERROR: unknown exit code\n"); exit(1); } } sipsak-0.9.8.1/src/exit_code.h000066400000000000000000000016101377576723000161030ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_EXITCODE_H #define SIPSAK_EXITCODE_H enum exit_modes { EM_DEFAULT, EM_NAGIOS }; extern enum exit_modes exit_mode; extern int sysl; void log_message(const char *message); void exit_code(int code, const char *function, const char *reason); #endif sipsak-0.9.8.1/src/header_f.c000066400000000000000000000373221377576723000157010ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #include "header_f.h" #include "exit_code.h" #include "helper.h" #include "shoot.h" #include "transport.h" /* add the given header(s) below the request line */ void insert_header(char *mes, char *header, int first) { char *ins, *backup; if (first) { ins = strchr(mes, '\n'); if (ins == NULL) { printf("failed to find a new line in the message\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to find a new line in the message"); } ins++; } else { ins = mes; } backup = str_alloc(strlen(ins) + 1); strncpy(backup, ins, strlen(ins)); strncpy(ins, header, strlen(header)); strncpy(ins + strlen(header), backup, strlen(backup)+1); free(backup); } /* add a Via Header Field in the message. */ void add_via(char *mes, char *fqdn, int lport) { char *via_line, *via, *backup; if ((via=STRCASESTR(mes, VIA_STR)) == NULL && (via=STRCASESTR(mes, VIA_SHORT_STR)) == NULL) { /* We didn't found a Via so we insert our via direct after the first line. */ via=strchr(mes,'\n'); if(via == NULL) { fprintf(stderr, "error: failed to find a position to insert Via:\n" "'%s'\n", mes); exit_code(2, __PRETTY_FUNCTION__, "failed to find position to insert to insert Via header"); } via++; } if (*via == '\n') via++; /* build our own Via-header-line */ via_line = str_alloc(VIA_SIP_STR_LEN+TRANSPORT_STR_LEN+1+ strlen(fqdn)+15+30+1); snprintf(via_line, VIA_SIP_STR_LEN+TRANSPORT_STR_LEN+1+strlen(fqdn)+15+30, "%s%s %s:%i;branch=z9hG4bK.%08x;rport;alias\r\n", VIA_SIP_STR, transport_str, fqdn, lport, rand()); if (verbose > 2) printf("our Via-Line: %s\n", via_line); if (strlen(mes)+strlen(via_line)>= BUFSIZE){ printf("can't add our Via Header Line because file is too big\n"); exit_code(2, __PRETTY_FUNCTION__, "Via header to big for buffer"); } /* finnaly make a backup, insert our via and append the backup */ backup=str_alloc((strlen(via)+1)); strncpy(backup, via, strlen(via)); strncpy(via, via_line, strlen(via_line)); strncpy(via+strlen(via_line), backup, strlen(backup)+1); if (verbose > 2) printf("New message with Via-Line:\n%s\n", mes); free(via_line); free(backup); } /* copy the via lines from the message to the message reply for correct routing of our reply. */ void cpy_vias(char *reply, char *dest){ char *first_via, *middle_via, *last_via, *backup; /* lets see if we find any via */ if ((first_via=STRCASESTR(reply, VIA_STR))==NULL && (first_via=STRCASESTR(reply, VIA_SHORT_STR))==NULL ){ fprintf(stderr, "error: the received message doesn't contain a Via header\n"); exit_code(3, __PRETTY_FUNCTION__, "missing Via header in message"); } last_via=first_via+4; /* proceed additional via lines */ while ((middle_via=STRCASESTR(last_via, VIA_STR))!=NULL || (middle_via=STRCASESTR(last_via, VIA_SHORT_STR))!=NULL ) last_via=middle_via+4; last_via=strchr(last_via, '\n'); middle_via=strchr(dest, '\n')+1; if (middle_via == NULL) { fprintf(stderr, "error: failed to locate end of middle Via header\n"); exit_code(3, __PRETTY_FUNCTION__, "missing end of Via header in message"); } /* make a backup, insert the vias after the first line and append backup */ backup=str_alloc(strlen(middle_via)+1); strcpy(backup, middle_via); strncpy(middle_via, first_via, (size_t)(last_via-first_via+1)); strcpy(middle_via+(last_via-first_via+1), backup); free(backup); if (verbose > 2) printf("message reply with vias included:\n%s\n", dest); } void cpy_to(char *reply, char *dest) { char *src_to, *dst_to, *backup, *tmp; /* find the position where we want to insert the To */ if ((dst_to=STRCASESTR(dest, TO_STR))==NULL && (dst_to=STRCASESTR(dest, TO_SHORT_STR))==NULL) { fprintf(stderr, "error: could not find To in the destination: %s\n", dest); exit_code(2, __PRETTY_FUNCTION__, "missing To header in target buffer"); } if (*dst_to == '\n') dst_to++; /* find the To we want to copy */ if ((src_to=STRCASESTR(reply, TO_STR))==NULL && (src_to=STRCASESTR(reply, TO_SHORT_STR))==NULL) { if (verbose > 0) fprintf(stderr, "warning: could not find To in reply. " "trying with original To\n"); } else { if (*src_to == '\n') src_to++; /* both To found, so copy it */ tmp=strchr(dst_to, '\n'); tmp++; backup=str_alloc(strlen(tmp)+1); strcpy(backup, tmp); tmp=strchr(src_to, '\n'); strncpy(dst_to, src_to, (size_t)(tmp-src_to+1)); strcpy(dst_to+(tmp-src_to+1), backup); free(backup); if (verbose >2) printf("reply with copied To:\n%s\n", dest); } } /* check for the existence of a Max-Forwards header field. if its present it sets it to the given value, if not it will be inserted.*/ void set_maxforw(char *mes, int value){ char *max, *backup, *crlfi; int maxforward; if ((max=STRCASESTR(mes, MAX_FRW_STR))==NULL){ /* no max-forwards found so insert it after the first line*/ max=strchr(mes,'\n'); if (!max) { printf("failed to find newline\n"); exit_code(254, __PRETTY_FUNCTION__, "missing newline in buffer"); } max++; backup=str_alloc(strlen(max)+1); strncpy(backup, max, (size_t)(strlen(max))); if (value == -1) { maxforward = 70; // RFC3261 default } else { maxforward = value; } snprintf(max, MAX_FRW_STR_LEN+6, "%s%i\r\n", MAX_FRW_STR, maxforward); max=strchr(max,'\n'); max++; strncpy(max, backup, strlen(backup)+1); free(backup); if (verbose > 1) printf("Max-Forwards %i inserted into header\n", maxforward); if (verbose > 2) printf("New message with inserted Max-Forwards:\n%s\n", mes); } else{ /* found max-forwards => overwrite the value with maxforw*/ crlfi=strchr(max,'\n'); crlfi++; backup=str_alloc(strlen(crlfi)+1); strncpy(backup, crlfi, strlen(crlfi)); crlfi=max + MAX_FRW_STR_LEN; if (value == -1) { maxforward = str_to_int(1, crlfi); maxforward++; } else { maxforward = value; } snprintf(crlfi, 6, "%i\r\n", maxforward); crlfi=strchr(max,'\n'); crlfi++; strncpy(crlfi, backup, strlen(backup)+1); free(backup); if (verbose > 1) printf("Max-Forwards set to %i\n", maxforward); if (verbose > 2) printf("New message with changed Max-Forwards:\n%s\n", mes); } } /* replaces the uri in first line of mes with the other uri */ void uri_replace(char *mes, char *uri) { char *foo, *backup; foo=strchr(mes, '\n'); if (!foo) { printf("failed to find newline\n"); exit_code(254, __PRETTY_FUNCTION__, "missing newline in buffer"); } foo++; backup=str_alloc(strlen(foo)+1); strncpy(backup, foo, strlen(foo)); foo=STRCASESTR(mes, "sip"); strncpy(foo, uri, strlen(uri)); strncpy(foo+strlen(uri), SIP20_STR, SIP20_STR_LEN); strncpy(foo+strlen(uri)+SIP20_STR_LEN, backup, strlen(backup)+1); free(backup); if (verbose > 2) printf("Message with modified uri:\n%s\n", mes); } /* replace the Content-Length value with the given value */ void set_cl(char* mes, int contentlen) { char *cl, *cr, *backup; if ((cl=STRCASESTR(mes, CON_LEN_STR)) == NULL && (cl=STRCASESTR(mes, CON_LEN_SHORT_STR)) == NULL) { printf("missing Content-Length in message\n"); return; } if (*cl == '\n') { cl++; } cr = strchr(cl, '\n'); cr++; backup=str_alloc(strlen(cr)+1); strncpy(backup, cr, strlen(cr)); if (*cl == 'C') cr=cl + CON_LEN_STR_LEN; else cr=cl + 3; snprintf(cr, 6, "%i\r\n", contentlen); cr=strchr(cr, '\n'); cr++; strncpy(cr, backup, strlen(backup)+1); free(backup); if (verbose > 2) { printf("Content-Length set to %i\n" "New message with changed Content-Length:\n%s\n", contentlen, mes); } } /* returns the content length from the message; in case of error it * return -1 */ int get_cl(char* mes) { char *cl; if ((cl=STRCASESTR(mes, CON_LEN_STR)) == NULL && (cl=STRCASESTR(mes, CON_LEN_SHORT_STR)) == NULL) { if (verbose > 1) printf("missing Content-Length in message\n"); return -1; } if (*cl == '\n') { cl+=3; } else { cl+=15; } return str_to_int(1, cl); } /* returns 1 if the rr_line contains the lr parameter * otherwise 0 */ int find_lr_parameter(char *rr_line) { char *eol, *lr; eol = strchr(rr_line, '\n'); lr = STRCASESTR(rr_line, ";lr"); if ((eol == NULL) || (lr == NULL) || (lr > eol)) { return 0; } else { return 1; } } /* copies the Record-Route header from src to dst. * if route is set Record-Route will be replaced by Route */ void cpy_rr(char* src, char *dst, int route) { char *rr, *cr, *cr2, *backup; int len; cr = strchr(dst, '\n'); if (cr == NULL) { fprintf(stderr, "error: failed to find newline in destination\n"); exit_code(3, __PRETTY_FUNCTION__, "missing newline in target buffer"); } cr++; rr = STRCASESTR(src, RR_STR); if (rr != NULL) { if (find_lr_parameter(rr) == 0) { fprintf(stderr, "error: strict routing is not support yet\n"); exit_code(252, __PRETTY_FUNCTION__, "strict routing is not supported"); } backup=str_alloc(strlen(cr)+1); strncpy(backup, cr, strlen(cr)); if (route == 0) len = RR_STR_LEN; else len = ROUTE_STR_LEN; while (rr != NULL) { if (route == 0) { strncpy(cr, RR_STR, RR_STR_LEN); } else { strncpy(cr, ROUTE_STR, ROUTE_STR_LEN); } cr += len; cr2 = strchr(rr, '\n'); if (cr2 == NULL) { fprintf(stderr, "error: failed to find end of line\n"); exit_code(3, __PRETTY_FUNCTION__, "missing newline in buffer"); } strncpy(cr, rr + RR_STR_LEN, (cr2 - (rr + len) + 1)); cr+=(cr2 - (rr + RR_STR_LEN) + 1); rr = STRCASESTR(++rr, RR_STR); } strncpy(cr, backup, strlen(backup)+1); free(backup); if (verbose > 2) printf("New message with inserted Route:\n%s\n", dst); } } /* build an ACK from the given invite and reply. * NOTE: space has to be allocated already for the ACK */ void build_ack(char *invite, char *reply, char *dest, struct sipsak_regexp *reg) { char *tmp; int len; if ((tmp = STRCASESTR(invite, "\r\n\r\n")) != NULL) { len = (tmp + 4) - invite; } else { len = strlen(invite); } memcpy(dest, invite, len); *(dest + len) = '\0'; replace_string(dest, "INVITE", "ACK"); set_cl(dest, 0); cpy_to(reply, dest); if (regexec(&(reg->okexp), reply, 0, 0, 0)==0) { cpy_rr(reply, dest, 1); /* 200 ACK must be in new transaction */ new_branch(dest); if((tmp = uri_from_contact(reply))!= NULL) { uri_replace(dest, tmp); free(tmp); } } } /* tryes to find the warning header filed and prints out the IP */ void warning_extract(char *message) { char *warning, *end, *mid, *server; int srvsize; if ((warning=STRCASESTR(message, "Warning:"))==NULL) { if (verbose > 0) printf("'no Warning header found' "); else printf("?? "); return; } end=strchr(warning, '"'); end--; warning=strchr(warning, '3'); warning+=4; mid=strchr(warning, ':'); if (mid) end=mid; srvsize=end - warning + 1; server=str_alloc((size_t)srvsize); server=strncpy(server, warning, (size_t)(srvsize - 1)); printf("%s ", server); free(server); } /* tries to find and return the number in the CSeq header */ int cseq(char *message) { char *cseq; int num; cseq=STRCASESTR(message, CSEQ_STR); if (cseq) { cseq+=6; num=str_to_int(1, cseq); if (num < 1) { if (verbose > 2) printf("CSeq found but not convertible\n"); return 0; } return num; } if (verbose > 2) printf("no CSeq found\n"); return 0; } /* if it find the Cseq number in the message it will increased by one */ int increase_cseq(char *message, char *reply) { int cs = 0; char *cs_s, *eol, *backup; cs = cseq(message); if ((cs < 1) && (verbose > 1)) { printf("CSeq increase failed because unable to extract CSeq number\n"); return 0; } if (cs == INT_MAX) cs = 1; else cs++; cs_s=STRCASESTR(message, CSEQ_STR); if (cs_s) { cs_s+=6; eol=strchr(cs_s, ' '); eol++; backup=str_alloc(strlen(eol)+1); strncpy(backup, eol, (size_t)(strlen(eol))); snprintf(cs_s, 11, "%i ", cs); cs_s+=strlen(cs_s); strncpy(cs_s, backup, strlen(backup)); free(backup); } else if (verbose > 1) printf("'CSeq' not found in message\n"); if (reply != NULL) { cs_s=STRCASESTR(reply, CSEQ_STR); if (cs_s) { cs_s+=6; eol=strchr(cs_s, ' '); eol++; backup=str_alloc(strlen(eol)+1); strncpy(backup, eol, (size_t)(strlen(eol))); snprintf(cs_s, 11, "%i ", cs); cs_s+=strlen(cs_s); strncpy(cs_s, backup, strlen(backup)); free(backup); } else if (verbose > 1) printf("'CSeq' not found in reply\n"); } return cs; } /* separates the given URI into the parts by setting the pointer but it destroyes the URI */ void parse_uri(char *uri, char **scheme, char **user, char **host, int *port) { char *col, *col2, *at; col = col2 = at = NULL; *port = 0; *scheme = *user = *host = NULL; if ((col=strchr(uri,':'))!=NULL) { if ((at=strchr(uri,'@'))!=NULL) { *col = '\0'; *at = '\0'; if (at > col) { *scheme = uri; *user = ++col; *host = ++at; if ((col2=strchr(*host,':'))!=NULL) { *col2 = '\0'; *port = str_to_int(1, ++col2); } } else { *user = uri; *host = ++at; *port = str_to_int(1, ++col); } } else { *col = '\0'; col++; if ((col2=strchr(col,':'))!=NULL) { *col2 = '\0'; *scheme = uri; *host = col; *port = str_to_int(1, ++col2); } else { if (is_number(col)) { *host = uri; *port = str_to_int(1, col); } else { *scheme = uri; *host = col; } } } } else { *host = uri; } } /* return a copy of the URI from the Contact of the message if found */ char* uri_from_contact(char *message) { char *contact, *end, *tmp, c; /* try to find the contact in the redirect */ if ((contact=STRCASESTR(message, CONT_STR))==NULL && (contact=STRCASESTR(message, CONT_SHORT_STR))==NULL ) { if(verbose > 1) printf("'Contact' not found in the message\n"); return NULL; } if (*contact == '\n') contact++; if((end=strchr(contact,'\r'))!=NULL) { c = '\r'; *end = '\0'; } else if((end=strchr(contact,'\n'))!=NULL) { c = '\n'; *end = '\0'; } else { c = '\0'; end = contact + strlen(contact); } tmp = NULL; if ((contact=STRCASESTR(contact, "sip:"))!=NULL) { if ((tmp=strchr(contact+4, ';'))!=NULL) { *end = c; end = tmp; c = *end; *end = '\0'; } if ((tmp=strchr(contact+4, '>'))!=NULL) { *end = c; end = tmp; c = *end; *end = '\0'; } tmp = str_alloc(strlen(contact)+1); memcpy(tmp,contact,strlen(contact)); } *end = c; return tmp; } /* replace the 8 bytes behind the first magic cookie with a new * random value */ void new_branch(char *message) { char *branch; char backup; if((branch = STRCASESTR(message,"branch=z9hG4bK.")) != NULL) { backup = *(branch+15+8); snprintf(branch+15, 9, "%08x", rand()); *(branch+15+8) = backup; } } /* increase the CSeq and insert a new branch value */ int new_transaction(char *message, char *reply) { new_branch(message); return increase_cseq(message, reply); } /* just print the first line of the message */ void print_message_line(char *message) { char *crlf; crlf=strchr(message, '\n'); if (!crlf) { printf("failed to find newline\n"); exit_code(254, __PRETTY_FUNCTION__, "missing newline in buffer"); } else if (*(crlf - 1) == '\r') crlf--; printf("%.*s\n", (int)(crlf - message), message); } /* return pointer to the beginning of the message body */ char* get_body(char *mes) { char *cr; if ((cr = strstr(mes, "\r\n\r\n")) != NULL) { cr+=4; } return cr; } sipsak-0.9.8.1/src/header_f.h000066400000000000000000000031031377576723000156740ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_HEADER_H #define SIPSAK_HEADER_H #include "shoot.h" void insert_header(char *mes, char *header, int first); void add_via(char *mes, char *fqdn, int lport); void cpy_vias(char *reply, char *dest); void cpy_to(char *reply, char *dest); void set_maxforw(char *mes, int value); void uri_replace(char *mes, char *uri); void set_cl(char* mes, int contentlen); int get_cl(char* mes); int find_lr_parameter(char *rr_line); void cpy_rr(char* src, char *dst, int route); void build_ack(char *invite, char *reply, char *dest, struct sipsak_regexp *reg); void warning_extract(char *message); int cseq(char *message); int increase_cseq(char *message, char *reply); void parse_uri(char *uri, char **scheme, char **user, char **host, int *port); char* uri_from_contact(char *message); void new_branch(char *message); int new_transaction(char *message, char *reply); void print_message_line(char *message); char* get_body(char *mes); #endif sipsak-0.9.8.1/src/helper.c000066400000000000000000000463041377576723000154230ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #ifdef HAVE_STDARG_H # include #endif #ifdef HAVE_NETDB_H # include #endif #ifdef HAVE_UNISTD_H # ifdef HAVE_SYS_TYPES_H # include # endif # include #endif #ifdef HAVE_SYS_UTSNAME_H # include #endif #ifdef HAVE_CTYPE_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_CARES_H # ifdef HAVE_ARPA_NAMESER_H # include # endif # include # ifndef NS_RRFIXEDSZ # define NS_RRFIXEDSZ 10 # define NS_QFIXEDSZ 4 # define NS_HFIXEDSZ 12 # endif int caport; unsigned long caadr; char *ca_tmpname; ares_channel channel; #endif // HAVE_CARES_H #include "helper.h" #include "exit_code.h" /* returns 1 if the string an IP address otherwise zero */ int is_ip(char *str) { int i = 0; int dotcount = 0; /*try understanding if this is a valid ip address we are skipping the values of the octets specified here. for instance, this code will allow 952.0.320.567 through*/ while (*str != '\0') { for (i = 0; i < 3; i++, str++) if (isdigit((int)*str) == 0) break; if (*str != '.') break; str++; dotcount++; } /* three dots with up to three digits in before, between and after ? */ if (*str == '\0' && dotcount == 3 && i > 0 && i <= 3) return 1; else return 0; } /* take either a dot.decimal string of ip address or a domain name and returns a NETWORK ordered long int containing the address. i chose to internally represent the address as long for speedier comparisons. any changes to getaddress have to be patched back to the net library. contact: farhan@hotfoon.com returns zero if there is an error. this is convenient as 0 means 'this' host and the traffic of a badly behaving dns system remains inside (you send to 0.0.0.0) */ unsigned long getaddress(char *host) { struct hostent* pent; long addr; if (strlen(host) == 0) { return 0; } if (is_ip(host)) { return inet_addr(host); } /* try the system's own resolution mechanism for dns lookup: required only for domain names. in spite of what the rfc2543 :D Using SRV DNS Records recommends, we are leaving it to the operating system to do the name caching. this is an important implementational issue especially in the light dynamic dns servers like dynip.com or dyndns.com where a dial ip address is dynamically assigned a sub domain like farhan.dynip.com although expensive, this is a must to allow OS to take the decision to expire the DNS records as it deems fit. */ pent = gethostbyname(host); if (!pent) { printf("'%s' is unresolvable\n", host); exit_code(2, __PRETTY_FUNCTION__, "hostname is not resolvable"); } addr = *(uint32_t *) (pent->h_addr); return addr; } #ifdef HAVE_CARES_H static const unsigned char *parse_rr(const unsigned char *aptr, const unsigned char *abuf, int alen) { char *name; long len; int status, type, dnsclass, dlen; struct in_addr addr; if (aptr == NULL) { return NULL; } dbg("ca_tmpname: %s\n", ca_tmpname); status = ares_expand_name(aptr, abuf, alen, &name, &len); if (status != ARES_SUCCESS) { printf("error: failed to expand query name\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to expand query name"); } aptr += len; if (aptr + NS_RRFIXEDSZ > abuf + alen) { printf("error: not enough data in DNS answer 1\n"); free(name); return NULL; } type = DNS_RR_TYPE(aptr); dnsclass = DNS_RR_CLASS(aptr); dlen = DNS_RR_LEN(aptr); aptr += NS_RRFIXEDSZ; if (aptr + dlen > abuf + alen) { printf("error: not enough data in DNS answer 2\n"); free(name); return NULL; } if (dnsclass != CARES_CLASS_C_IN) { printf("error: unsupported dnsclass (%i) in DNS answer\n", dnsclass); free(name); return NULL; } if (type != CARES_TYPE_SRV && type != CARES_TYPE_A && type != CARES_TYPE_CNAME) { printf("error: unsupported DNS response type (%i)\n", type); free(name); return NULL; } if (type == CARES_TYPE_SRV) { free(name); caport = DNS__16BIT(aptr + 4); dbg("caport: %i\n", caport); status = ares_expand_name(aptr + 6, abuf, alen, &name, &len); if (status != ARES_SUCCESS) { printf("error: failed to expand SRV name\n"); return NULL; } dbg("SRV name: %s\n", name); if (is_ip(name)) { caadr = inet_addr(name); free(name); } else { ca_tmpname = name; } } else if (type == CARES_TYPE_CNAME) { if ((ca_tmpname != NULL) && (STRNCASECMP(ca_tmpname, name, strlen(ca_tmpname)) == 0)) { ca_tmpname = malloc(strlen(name) + 1); if (ca_tmpname == NULL) { printf("error: failed to allocate memory\n"); exit_code(2, __PRETTY_FUNCTION__, "memory allocation failure"); } strcpy(ca_tmpname, name); free(name); } else { free(name); status = ares_expand_name(aptr, abuf, alen, &name, &len); if (status != ARES_SUCCESS) { printf("error: failed to expand CNAME\n"); return NULL; } dbg("CNAME: %s\n", name); if (is_ip(name)) { caadr = inet_addr(name); free(name); } else { ca_tmpname = name; } } } else if (type == CARES_TYPE_A) { if (dlen == 4 && STRNCASECMP(ca_tmpname, name, strlen(ca_tmpname)) == 0) { memcpy(&addr, aptr, sizeof(struct in_addr)); caadr = addr.s_addr; } free(name); } return aptr + dlen; } static const unsigned char *skip_rr(const unsigned char *aptr, const unsigned char *abuf, int alen) { int status, dlen; long len; char *name; if (aptr == NULL) { return NULL; } dbg("skipping rr section...\n"); status = ares_expand_name(aptr, abuf, alen, &name, &len); if (status != ARES_SUCCESS) { return NULL; } aptr += len; dlen = DNS_RR_LEN(aptr); aptr += NS_RRFIXEDSZ; aptr += dlen; free(name); return aptr; } static const unsigned char *skip_query(const unsigned char *aptr, const unsigned char *abuf, int alen) { int status; long len; char *name; if (aptr == NULL) { return NULL; } dbg("skipping query section...\n"); status = ares_expand_name(aptr, abuf, alen, &name, &len); if (status != ARES_SUCCESS) { return NULL; } aptr += len; aptr += NS_QFIXEDSZ; free(name); return aptr; } static void cares_callback(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { int i; unsigned int ancount, nscount, arcount; const unsigned char *aptr; dbg("cares_callback: status=%i, alen=%i\n", status, alen); if (status != ARES_SUCCESS) { if (verbose > 1) printf("ares failed: %s\n", ares_strerror(status)); return; } ancount = DNS_HEADER_ANCOUNT(abuf); nscount = DNS_HEADER_NSCOUNT(abuf); arcount = DNS_HEADER_ARCOUNT(abuf); dbg("ancount: %i, nscount: %i, arcount: %i\n", ancount, nscount, arcount); /* safety check */ if (alen < NS_HFIXEDSZ) return; aptr = abuf + NS_HFIXEDSZ; aptr = skip_query(aptr, abuf, alen); if (aptr == NULL) { return; } for (i = 0; i < ancount && caadr == 0 && aptr != NULL; i++) { if (ca_tmpname == NULL) aptr = parse_rr(aptr, abuf, alen); else aptr = skip_rr(aptr, abuf, alen); } if (caadr == 0 && aptr != NULL) { for (i = 0; i < nscount && aptr != NULL; i++) { aptr = skip_rr(aptr, abuf, alen); } for (i = 0; i < arcount && caadr == 0; i++) { aptr = parse_rr(aptr, abuf, alen); } } } static inline unsigned long srv_ares(char *host, int *port, char *srv) { int nfds, count, srvh_len; char *srvh; fd_set read_fds, write_fds; struct timeval *tvp, tv; caport = 0; caadr = 0; ca_tmpname = NULL; dbg("starting ARES query\n"); srvh_len = strlen(host) + strlen(srv) + 2; srvh = malloc(srvh_len); if (srvh == NULL) { printf("error: failed to allocate memory (%i) for ares query\n", srvh_len); exit_code(2, __PRETTY_FUNCTION__, "memory allocation failure"); } memset(srvh, 0, srvh_len); strncpy(srvh, srv, strlen(srv)); memcpy(srvh + strlen(srv), ".", 1); strcpy(srvh + strlen(srv) + 1, host); dbg("hostname: '%s', len: %i\n", srvh, srvh_len); ares_query(channel, srvh, CARES_CLASS_C_IN, CARES_TYPE_SRV, cares_callback, (char *) NULL); dbg("ares_query finished, waiting for result...\n"); /* wait for query to complete */ while (1) { FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(channel, &read_fds, &write_fds); if (nfds == 0) break; tvp = ares_timeout(channel, NULL, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); if (count < 0 && errno != EINVAL) { perror("ares select"); exit_code(2, __PRETTY_FUNCTION__, "ares DNS resolution failure"); } ares_process(channel, &read_fds, &write_fds); } dbg("ARES answer processed\n"); *port = caport; if (caadr == 0 && ca_tmpname != NULL) { caadr = getaddress(ca_tmpname); } if (ca_tmpname != NULL) free(ca_tmpname); free(srvh); return caadr; } #endif // HAVE_CARES_H unsigned long getsrvaddress(char *host, int *port, char *srv) { #ifdef HAVE_CARES_H return srv_ares(host, port, srv); #else // HAVE_CARES_H return 0; #endif } /* Finds the SRV records for the given host. It returns the target IP * address and fills the port and transport if a suitable SRV record * exists. Otherwise it returns 0. The function follows 3263: first * TLS, then TCP and finally UDP. */ unsigned long getsrvadr(char *host, int *port, unsigned int *transport) { unsigned long adr = 0; #ifdef HAVE_SRV int srvport = 5060; #ifdef HAVE_CARES_H int status; int optmask = ARES_OPT_FLAGS; struct ares_options options; options.flags = ARES_FLAG_NOCHECKRESP; options.servers = NULL; options.nservers = 0; status = ares_init_options(&channel, &options, optmask); if (status != ARES_SUCCESS) { printf("error: failed to initialize ares\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to init ares lib"); } #endif #ifdef WITH_TLS_TRANSP adr = getsrvaddress(host, &srvport, SRV_SIP_TLS); if (adr != 0) { *transport = SIP_TLS_TRANSPORT; if (verbose > 1) printf("using SRV record: %s.%s:%i\n", SRV_SIP_TLS, host, srvport); } else { #endif adr = getsrvaddress(host, &srvport, SRV_SIP_TCP); if (adr != 0) { *transport = SIP_TCP_TRANSPORT; if (verbose > 1) printf("using SRV record: %s.%s:%i\n", SRV_SIP_TCP, host, srvport); } else { adr = getsrvaddress(host, &srvport, SRV_SIP_UDP); if (adr != 0) { *transport = SIP_UDP_TRANSPORT; if (verbose > 1) printf("using SRV record: %s.%s:%i\n", SRV_SIP_UDP, host, srvport); } } #ifdef WITH_TLS_TRANSP } #endif #ifdef HAVE_CARES_H ares_destroy(channel); #endif *port = srvport; #endif // HAVE_SRV return adr; } /* because the full qualified domain name is needed by many other functions it will be determined by this function. */ void get_fqdn(char *buf, int numeric, char *hostname) { char hname[100], dname[100], hlp[18]; size_t namelen=100; struct hostent* he; struct utsname un; memset(&hname, 0, sizeof(hname)); memset(&dname, 0, sizeof(dname)); memset(&hlp, 0, sizeof(hlp)); if (hostname) { strncpy(buf, hostname, FQDN_SIZE-1); strncpy(hname, hostname, sizeof(hname)-1); } else { if ((uname(&un))==0) { strncpy(hname, un.nodename, sizeof(hname)-1); } else { if (gethostname(&hname[0], namelen) < 0) { fprintf(stderr, "error: cannot determine hostname\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to determine hostname"); } } #ifdef HAVE_GETDOMAINNAME /* a hostname with dots should be a domainname */ if ((strchr(hname, '.'))==NULL) { if (getdomainname(&dname[0], namelen) < 0) { fprintf(stderr, "error: cannot determine domainname\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to get domainname"); } if (strcmp(&dname[0],"(none)")!=0) snprintf(buf, FQDN_SIZE, "%s.%s", hname, dname); } else { strncpy(buf, hname, FQDN_SIZE-1); } #endif } if (!(numeric == 1 && is_ip(buf))) { he=gethostbyname(hname); if (he) { if (numeric == 1) { snprintf(hlp, sizeof(hlp), "%s", inet_ntoa(*(struct in_addr *) he->h_addr_list[0])); strncpy(buf, hlp, FQDN_SIZE-1); } else { if ((strchr(he->h_name, '.'))!=NULL && (strchr(hname, '.'))==NULL) { strncpy(buf, he->h_name, FQDN_SIZE-1); } else { strncpy(buf, hname, FQDN_SIZE-1); } } } else { fprintf(stderr, "error: cannot resolve local hostname: %s\n", hname); exit_code(2, __PRETTY_FUNCTION__, "failed to resolve local hostname"); } } if ((strchr(buf, '.'))==NULL) { if (hostname) { fprintf(stderr, "warning: %s is not resolvable... continuing anyway\n", buf); strncpy(buf, hostname, FQDN_SIZE-1); } else { fprintf(stderr, "error: this FQDN or IP is not valid: %s\n", buf); exit_code(2, __PRETTY_FUNCTION__, "invalid IP or FQDN"); } } if (verbose > 2) printf("fqdnhostname: %s\n", buf); } /* this function searches for search in mess and replaces it with replacement */ void replace_string(char *mess, char *search, char *replacement) { char *backup, *insert; insert=STRCASESTR(mess, search); if (insert==NULL){ if (verbose > 2) fprintf(stderr, "warning: could not find this '%s' replacement string in " "message\n", search); } else { while (insert){ backup=str_alloc(strlen(insert)+1); strcpy(backup, insert+strlen(search)); strcpy(insert, replacement); strcpy(insert+strlen(replacement), backup); free(backup); insert=STRCASESTR(mess, search); } } } /* checks if the strings contains special double marks and then * replace all occurrences of this strings in the message */ void replace_strings(char *mes, char *strings) { char *pos, *atr, *val, *repl, *end; char sep; pos=atr=val=repl = NULL; dbg("replace_strings entered\nstrings: '%s'\n", strings); if ((isalnum(*strings) != 0) && (isalnum(*(strings + strlen(strings) - 1)) != 0)) { replace_string(mes, "$replace$", strings); } else { sep = *strings; dbg("sep: '%c'\n", sep); end = strings + strlen(strings); pos = strings + 1; while (pos < end) { atr = pos; pos = strchr(atr, sep); if (pos != NULL) { *pos = '\0'; val = pos + 1; pos = strchr(val, sep); if (pos != NULL) { *pos = '\0'; pos++; } } dbg("atr: '%s'\nval: '%s'\n", atr, val); if ((atr != NULL) && (val != NULL)) { repl = str_alloc(strlen(val) + 3); if (repl == NULL) { printf("failed to allocate memory\n"); exit_code(2, __PRETTY_FUNCTION__, "memory allocation failure"); } sprintf(repl, "$%s$", atr); replace_string(mes, repl, val); free(repl); } dbg("pos: '%s'\n", pos); } } dbg("mes:\n'%s'\n", mes); } /* insert \r in front of all \n if it is not present already * and and a trailing \r\n is not present */ void insert_cr(char *mes) { char *lf, *pos, *backup; pos = mes; lf = strchr(pos, '\n'); while ((lf != NULL) && (lf >= mes+1) && (*(--lf) != '\r')) { backup=str_alloc(strlen(lf)+2); strcpy(backup, lf+1); *(lf+1) = '\r'; strcpy(lf+2, backup); free(backup); pos = lf+3; lf = strchr(pos, '\n'); } lf = STRCASESTR(mes, "\r\n\r\n"); if (lf == NULL) { lf = mes + strlen(mes); sprintf(lf, "\r\n"); } } /* sipmly swappes the content of the two buffers */ void swap_buffers(char *fst, char *snd) { char *tmp; if (fst == snd) return; tmp = str_alloc(strlen(fst)+1); strcpy(tmp, fst); strcpy(fst, snd); strcpy(snd, tmp); free(tmp); } void swap_ptr(char **fst, char **snd) { char *tmp; tmp = *fst; *fst = *snd; *snd = tmp; } /* trashes one character in buff randomly */ void trash_random(char *message) { int r; float t; char *position; t=(float)rand()/RAND_MAX; r=(int)(t * (float)strlen(message)); position=message+r; r=(int)(t*(float)255); *position=(char)r; if (verbose > 2) printf("request:\n%s\n", message); } /* this function is taken from traceroute-1.4_p12 which is distributed under the GPL and it returns the difference between to timeval structs */ double deltaT(struct timeval *t1p, struct timeval *t2p) { register double dt; dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; return (dt); } /* returns one if the string contains only numbers otherwise zero */ int is_number(char *number) { int digit = 1; if (strlen(number) == 0) { return 0; } while (digit && (*number != '\0')) { digit = isdigit(*number); number++; } return digit ? 1 : 0; } /* tries to convert the given string into an integer. it strips * white-spaces and exits if an error happens */ int str_to_int(int mode, char *num) { int ret, len; char *end, *start; char *backup = NULL; len = strlen(num); if (len == 0) { fprintf(stderr, "error: string has zero length: '%s'\n", num); ret = 2; goto error; } /* we need to make a backup to insert the zero char */ backup = malloc(len + 1); if (!backup) { fprintf(stderr, "error: failed to allocate memory\n"); ret = 2; goto error; } memcpy(backup, num, len + 1); start = backup; end = backup + len; while (isspace(*start) && (start < end)) { start++; } if (start == end) { fprintf(stderr, "error: string is too short: '%s'\n", num); ret = 2; goto error; } if (mode == 0) { end--; while (isspace(*end) && (end > start)) { end--; } if (end != (backup + len - 1)) { end++; *end = '\0'; } } else { end = start; end++; while ((end < backup + len) && *end != '\0' && !isspace(*end)) { end++; } *end = '\0'; } if (!is_number(start)) { fprintf(stderr, "error: string is not a number: '%s'\n", start); ret = 2; goto error; } ret = atoi(start); if (ret >= 0) { free(backup); return ret; } else { fprintf(stderr, "error: failed to convert string to integer: '%s'\n", num); ret = 2; } error: if (backup) { free(backup); } if (mode == 0) { /* libcheck expects a return value not an exit code */ #ifndef RUNNING_CHECK exit_code(ret, __PRETTY_FUNCTION__, NULL); #endif } return (ret * - 1); } /* reads into the given buffer from standard input until the EOF * character, LF character or the given size of the buffer is exceeded */ int read_stdin(char *buf, int size, int ret) { int i, j; for(i = 0; i < size - 1; i++) { j = getchar(); if (((ret == 0) && (j == EOF)) || ((ret == 1) && (j == '\n'))) { *(buf + i) = '\0'; return i; } else { *(buf + i) = j; } } *(buf + i) = '\0'; if (verbose) fprintf(stderr, "warning: readin buffer size exceeded\n"); return i; } /* tries to allocate the given size of memory and sets it all to zero. * if the allocation fails it exits */ void *str_alloc(size_t size) { char *ptr; #ifdef HAVE_CALLOC ptr = calloc(1, size); #else ptr = malloc(size); #endif if (ptr == NULL) { fprintf(stderr, "error: memory allocation for %lu bytes failed\n", size); exit_code(255, __PRETTY_FUNCTION__, "memory allocation failure"); } #ifndef HAVE_CALLOC memset(ptr, 0, size); #endif return ptr; } void dbg(char* format, ...) { #ifdef DEBUG va_list ap; fprintf(stderr, "DEBUG: "); va_start(ap, format); vfprintf(stderr, format, ap); fflush(stderr); va_end(ap); #endif } sipsak-0.9.8.1/src/helper.h000066400000000000000000000044551377576723000154310ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_HELPER_H #define SIPSAK_HELPER_H #include "sipsak.h" #ifdef HAVE_SYS_TIME_H # include #else # include #endif #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_CARES_H # define HAVE_SRV #else # ifdef HAVE_RULI_H # define HAVE_SRV # endif #endif #ifdef HAVE_CARES_H # define CARES_TYPE_A 1 # define CARES_TYPE_CNAME 5 # define CARES_TYPE_SRV 33 # define CARES_CLASS_C_IN 1 /* copied from ares_dns.h */ # define DNS__16BIT(p) (((p)[0] << 8) | (p)[1]) # define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6) # define DNS_HEADER_NSCOUNT(h) DNS__16BIT((h) + 8) # define DNS_HEADER_ARCOUNT(h) DNS__16BIT((h) + 10) # define DNS_RR_TYPE(r) DNS__16BIT(r) # define DNS_RR_CLASS(r) DNS__16BIT((r) + 2) # define DNS_RR_LEN(r) DNS__16BIT((r) + 8) #endif #ifdef HAVE_SRV # define SRV_SIP_TLS "_sip._tls" # define SRV_SIP_TCP "_sip._tcp" # define SRV_SIP_UDP "_sip._udp" #endif int is_ip(char *str); unsigned long getaddress(char *host); unsigned long getsrvadr(char *host, int *port, unsigned int *transport); void get_fqdn(char *buf, int numeric, char *hostname); void replace_string(char *mes, char *search, char *replacement); void replace_strings(char *mes, char *strings); void insert_cr(char *mes); void swap_buffers(char *fst, char *snd); void swap_ptr(char **fst, char **snd); void trash_random(char *message); double deltaT(struct timeval *t1p, struct timeval *t2p); int is_number(char *number); int str_to_int(int mode, char *num); int read_stdin(char *buf, int size, int ret); void *str_alloc(size_t size); void dbg(char* format, ...); #endif sipsak-0.9.8.1/src/md5.c000066400000000000000000000247211377576723000146300ustar00rootroot00000000000000/* 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "md5.h" #ifndef HAVE_EXTERNAL_MD5 #ifdef HAVE_STRING_H # include #endif #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. */ void MD5Init (context) MD5_CTX *context; /* 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. */ void MD5Update (context, input, inputLen) MD5_CTX *context; /* context */ unsigned char *input; /* input block */ unsigned int inputLen; /* length of input block */ { 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. */ void MD5Final (digest, context) unsigned char digest[16]; /* message digest */ MD5_CTX *context; /* 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 (state, block) 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 (output, input, len) 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 (output, input, len) 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 (output, input, len) 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 (output, value, len) 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 } #endif /* ! HAVE_EXTERNAL_MD5 */ sipsak-0.9.8.1/src/md5.h000066400000000000000000000032231377576723000146270ustar00rootroot00000000000000/* 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. */ #ifndef SIPSAK_MD5_H #define SIPSAK_MD5_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "sipsak.h" #ifdef HAVE_FULL_OPENSSL # include #endif #ifdef HAVE_EXTERNAL_MD5 # define MD5Init MD5_Init # define MD5Update MD5_Update # define MD5Final MD5_Final #else #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 #endif sipsak-0.9.8.1/src/md5global.h000066400000000000000000000014321377576723000160100ustar00rootroot00000000000000/* 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 PROTOTYPES #define PROTOTYPES 0 #endif /* POINTER defines a generic pointer type */ typedef unsigned char *POINTER; #ifdef HAVE_STDINT_H # include typedef uint32_t UINT4; #else /* UINT4 defines a four byte word */ typedef unsigned long int UINT4; #endif /* 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 sipsak-0.9.8.1/src/request.c000066400000000000000000000225601377576723000156320ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #ifdef HAVE_STRING_H # include #endif #include "request.h" #include "exit_code.h" #include "helper.h" #include "header_f.h" #include "transport.h" /* create a valid sip header for the different modes */ void create_msg(int action, struct sipsak_msg_data *msg_data){ unsigned int c, d, len; char *req_buf_begin = msg_data->req_buff; if(msg_data->cseq_counter == 0) { fprintf(stderr, "error: CSeq 0 is not allowed\n"); exit_code(253, __PRETTY_FUNCTION__, "invalid CSeq 0"); } if (msg_data->req_buff == NULL) abort(); if (msg_data->username == NULL) msg_data->username = ""; c=(unsigned int)rand(); c+=msg_data->lport; d=(unsigned int)rand(); switch (action){ case REQ_REG: sprintf(msg_data->req_buff, "%s sip:%s%s" "%ssip:%s%s;tag=%x\r\n" "%ssip:%s%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%s0\r\n" "%s70\r\n" "%s%s\r\n", REG_STR, msg_data->domainname, SIP20_STR, FROM_STR, msg_data->username, msg_data->domainname, c, TO_STR, msg_data->username, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, REG_STR, CON_LEN_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR); msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->contact_uri!=NULL) { sprintf(msg_data->req_buff, "%s%i\r\n" "%s%s\r\n\r\n", EXP_STR, msg_data->expires_t, CONT_STR, msg_data->contact_uri); } else if (msg_data->empty_contact == 0) { sprintf(msg_data->req_buff, "%s%i\r\n" "%ssip:%s%s:%i", EXP_STR, msg_data->expires_t, CONT_STR, msg_data->username, msg_data->fqdn, msg_data->lport); msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->transport != SIP_UDP_TRANSPORT) sprintf(msg_data->req_buff, "%s%s\r\n\r\n", TRANSPORT_PARAMETER_STR, transport_str); else sprintf(msg_data->req_buff, "\r\n\r\n"); } else{ sprintf(msg_data->req_buff, "\r\n"); } add_via(req_buf_begin, msg_data->fqdn, msg_data->lport); break; case REQ_REM: sprintf(msg_data->req_buff, "%s sip:%s%s" "%ssip:%s%s;tag=%x\r\n" "%ssip:%s%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%s%i\r\n" "%s0\r\n" "%s70\r\n" "%s%s\r\n" "%ssip:%s%s:%i;%s0", REG_STR, msg_data->domainname, SIP20_STR, FROM_STR, msg_data->username, msg_data->domainname, c, TO_STR, msg_data->username, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, REG_STR, EXP_STR, msg_data->expires_t, CON_LEN_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR, CONT_STR, msg_data->username, msg_data->fqdn, msg_data->lport, CON_EXP_STR); msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->transport != SIP_UDP_TRANSPORT) { sprintf(msg_data->req_buff, "\r\n\r\n"); } else { sprintf(msg_data->req_buff, "%s%s\r\n\r\n", TRANSPORT_PARAMETER_STR, transport_str); } add_via(req_buf_begin, msg_data->fqdn, msg_data->lport); break; case REQ_INV: sprintf(msg_data->req_buff, "%s sip:%s%s%s" "%ssip:%s%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%s0\r\n" "%ssip:sipsak@%s:%i\r\n" "%sDONT ANSWER this test call!\r\n" "%s70\r\n" "%s%s\r\n", INV_STR, msg_data->username, msg_data->domainname, SIP20_STR, TO_STR, msg_data->username, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, INV_STR, CON_LEN_STR, CONT_STR, msg_data->fqdn, msg_data->lport, SUB_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR); msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->from_uri) { sprintf(msg_data->req_buff, "%s%s;tag=%x\r\n" "\r\n", FROM_STR, msg_data->from_uri, c); } else { sprintf(msg_data->req_buff, "%ssip:sipsak@%s:%i;tag=%x\r\n" "\r\n", FROM_STR, msg_data->fqdn, msg_data->lport, c); } add_via(req_buf_begin, msg_data->fqdn, msg_data->lport); sprintf(msg_data->repl_buff, "%s" "%ssip:sipsak@%s:%i;tag=%x\r\n" "%ssip:%s%s;tag=%o%o\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%s0\r\n" "%ssip:sipsak_conf@%s:%i\r\n" "%s%s\r\n" "\r\n", SIP200_STR, FROM_STR, msg_data->fqdn, msg_data->lport, c, TO_STR, msg_data->username, msg_data->domainname, c, d, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, INV_STR, CON_LEN_STR, CONT_STR, msg_data->fqdn, msg_data->lport, UA_STR, UA_VAL_STR); break; case REQ_MES: sprintf(msg_data->req_buff, "%s sip:%s%s%s" "%ssip:%s%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%s%s\r\n" "%s70\r\n" "%s%s\r\n", MES_STR, msg_data->username, msg_data->domainname, SIP20_STR, TO_STR, msg_data->username, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, MES_STR, CON_TYP_STR, TXT_PLA_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR); msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->from_uri) { sprintf(msg_data->req_buff, "%s%s;tag=%x\r\n", FROM_STR, msg_data->from_uri, c); } else { sprintf(msg_data->req_buff, "%ssip:sipsak@%s:%i;tag=%x\r\n", FROM_STR, msg_data->fqdn, msg_data->lport, c); } msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->mes_body) { len = strlen(msg_data->mes_body); } else { len = SIPSAK_MES_STR_LEN + strlen(msg_data->username); } sprintf(msg_data->req_buff, "%s%u\r\n", CON_LEN_STR, len); msg_data->req_buff += strlen(msg_data->req_buff); if (msg_data->con_dis) { sprintf(msg_data->req_buff, "%s%s\r\n", CON_DIS_STR, msg_data->con_dis); msg_data->req_buff += strlen(msg_data->req_buff); } sprintf(msg_data->req_buff, "\r\n"); msg_data->req_buff += 2; if (msg_data->mes_body) { sprintf(msg_data->req_buff, "%s", msg_data->mes_body); } else { sprintf(msg_data->req_buff, "%s%s", SIPSAK_MES_STR, msg_data->username); msg_data->req_buff += strlen(msg_data->req_buff) - 1; *(msg_data->req_buff) = '.'; } add_via(req_buf_begin, msg_data->fqdn, msg_data->lport); sprintf(msg_data->repl_buff, "%s" "%ssip:sipsak@%s:%i;tag=%x\r\n" "%ssip:%s%s;tag=%o%o\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%s0\r\n" "%s%s\r\n" "\r\n", SIP200_STR, FROM_STR, msg_data->fqdn, msg_data->lport, c, TO_STR, msg_data->username, msg_data->domainname, c, d, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, MES_STR, CON_LEN_STR, UA_STR, UA_VAL_STR); break; case REQ_OPT: sprintf(msg_data->req_buff, "%s sip:%s%s%s" "%ssip:sipsak@%s:%i;tag=%x\r\n" "%ssip:%s%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%ssip:sipsak@%s:%i\r\n" "%s0\r\n" "%s70\r\n" "%s%s\r\n" "%s%s\r\n" "\r\n", OPT_STR, msg_data->username, msg_data->domainname, SIP20_STR, FROM_STR, msg_data->fqdn, msg_data->lport, c, TO_STR, msg_data->username, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, OPT_STR, CONT_STR, msg_data->fqdn, msg_data->lport, CON_LEN_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR, ACP_STR, TXT_PLA_STR); add_via(req_buf_begin, msg_data->fqdn, msg_data->lport); break; case REQ_FLOOD: sprintf(msg_data->req_buff, "%s sip:%s%s%s" "%s%s %s:9;branch=z9hG4bK.%08x\r\n" "%ssip:sipsak@%s:9;tag=%x\r\n" "%ssip:%s%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%ssip:sipsak@%s:9\r\n" "%s0\r\n" "%s70\r\n" "%s%s\r\n" "\r\n", FLOOD_METH, msg_data->username, msg_data->domainname, SIP20_STR, VIA_SIP_STR, TRANSPORT_UDP_STR, msg_data->fqdn, d, FROM_STR, msg_data->fqdn, c, TO_STR, msg_data->username, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, FLOOD_METH, CONT_STR, msg_data->fqdn, CON_LEN_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR); break; case REQ_RAND: sprintf(msg_data->req_buff, "%s sip:%s%s" "%ssip:sipsak@%s:%i;tag=%x\r\n" "%ssip:%s\r\n" "%s%u@%s\r\n" "%s%i %s\r\n" "%ssipsak@%s:%i\r\n" "%s0\r\n" "%s70\r\n" "%s%s\r\n" "\r\n", OPT_STR, msg_data->domainname, SIP20_STR, FROM_STR, msg_data->fqdn, msg_data->lport, c, TO_STR, msg_data->domainname, CALL_STR, c, msg_data->fqdn, CSEQ_STR, msg_data->cseq_counter, OPT_STR, CONT_STR, msg_data->fqdn, msg_data->lport, CON_LEN_STR, MAX_FRW_STR, UA_STR, UA_VAL_STR); add_via(req_buf_begin, msg_data->fqdn, msg_data->lport); break; default: fprintf(stderr, "error: unknown request type to create\n"); exit_code(2, __PRETTY_FUNCTION__, "unknown request type requested"); break; } if (msg_data->headers) { insert_header(req_buf_begin, msg_data->headers, 1); if (msg_data->repl_buff) insert_header(msg_data->repl_buff, msg_data->headers, 1); } } sipsak-0.9.8.1/src/request.h000066400000000000000000000020461377576723000156340ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_REQUEST_H #define SIPSAK_REQUEST_H struct sipsak_msg_data { int cseq_counter; int lport; int expires_t; int empty_contact; unsigned int transport; char *req_buff; char *repl_buff; char *username; char *domainname; char *contact_uri; char *con_dis; char *from_uri; char *mes_body; char *headers; char *fqdn; }; void create_msg(int action, struct sipsak_msg_data *data); #endif sipsak-0.9.8.1/src/shoot.c000066400000000000000000001114301377576723000152710ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif /* TIME_WITH_SYS_TIME */ #ifdef HAVE_UNISTD_H # ifdef HAVE_SYS_TYPES_H # include # endif # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #include "shoot.h" #include "request.h" #include "auth.h" #include "header_f.h" #include "helper.h" #include "exit_code.h" #include "transport.h" #ifndef DEFAULT_RETRYS #define DEFAULT_RETRYS 5 #endif #ifndef DEFAULT_TIMEOUT #define DEFAULT_TIMEOUT 5000 #endif char *request; char *response; char *received; char *usern; int inv_trans; char fqdn[FQDN_SIZE]; enum usteps usrlocstep; struct sipsak_regexp regexps; struct sipsak_sr_time timers; struct sipsak_con_data cdata; struct sipsak_counter counters; struct sipsak_delay delays; struct sipsak_msg_data msg_data; /* if a reply was received successfully, return success, unless * reply matching is enabled and no match occurred */ static inline void on_success(char *_response, regex_t *regex) { if ((_response != NULL) && regex && regexec(regex, _response, 0, 0, 0) == REG_NOMATCH) { log_message(request); fprintf(stderr, "error: RegExp failed\n"); exit_code(32, __PRETTY_FUNCTION__, "regular expression failed"); } else { exit_code(0, __PRETTY_FUNCTION__, NULL); } } /* just print the given username and number into the first buffer and * append an @ char */ static inline void create_usern(char *target, char *username, int number) { if (number >= 0) { sprintf(target, "%s%i@", username, number); } else { sprintf(target, "%s@", username); } } /* tries to take care of a redirection */ void handle_3xx(struct sockaddr_in *tadr, int warning_ext, int rport, unsigned long address, unsigned int transport, int outbound_proxy, char *domainname #ifdef WITH_TLS_TRANSP , int ignore_ca_fail #endif ) { char *uscheme, *uuser, *uhost, *contact; printf("** received redirect "); if (warning_ext == 1) { printf("from "); warning_extract(received); printf("\n"); } else printf("\n"); /* we'll try to handle 301 and 302 here, other 3xx are to complex */ regcomp(&(regexps.redexp), "^SIP/[0-9]\\.[0-9] 30[125] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); if (regexec(&(regexps.redexp), received, 0, 0, 0) == REG_NOERROR) { /* try to find the contact in the redirect */ contact = uri_from_contact(received); if (contact==NULL) { fprintf(stderr, "error: cannot find Contact in this " "redirect:\n%s\n", received); exit_code(3, __PRETTY_FUNCTION__, "missing Contact header in reply"); } /* correct our request */ uri_replace(request, contact); msg_data.cseq_counter = new_transaction(request, response); /* extract the needed information*/ rport = 0; address = 0; parse_uri(contact, &uscheme, &uuser, &uhost, &rport); if (!rport) address = getsrvadr(uhost, &rport, &transport); if (!address) address = getaddress(uhost); if (!address){ fprintf(stderr, "error: cannot determine host " "address from Contact of redirect:" "\n%s\n", received); exit_code(2, __PRETTY_FUNCTION__, "missing host in Contact header"); } if (!rport) { rport = 5060; } free(contact); if (!outbound_proxy) cdata.connected = set_target(tadr, address, rport, cdata.csock, cdata.connected, cdata.transport, domainname #ifdef WITH_TLS_TRANSP , ignore_ca_fail #endif ); } else { fprintf(stderr, "error: cannot handle this redirect:" "\n%s\n", received); exit_code(2, __PRETTY_FUNCTION__, "unsupported redirect reply"); } } /* takes care of replies in the trace route mode */ void trace_reply(regex_t *regex, int namebeg, struct sipsak_sr_time *timers) { char *contact; if (regexec(&(regexps.tmhexp), received, 0, 0, 0) == REG_NOERROR) { /* we received 483 to many hops */ printf("%i: ", namebeg); if (verbose > 2) { printf("(%.3f ms)\n%s\n", deltaT(&(timers->sendtime), &(timers->recvtime)), received); } else { warning_extract(received); printf("(%.3f ms) ", deltaT(&(timers->sendtime), &(timers->recvtime))); print_message_line(received); } // FIXME looks like we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_msg(REQ_OPT, &msg_data); set_maxforw(request, namebeg); return; } else if (regexec(&(regexps.proexp), received, 0, 0, 0) == REG_NOERROR) { /* we received a provisional response */ printf("%i: ", namebeg); if (verbose > 2) { printf("(%.3f ms)\n%s\n", deltaT(&(timers->sendtime), &(timers->recvtime)), received); } else { warning_extract(received); printf("(%.3f ms) ", deltaT(&(timers->sendtime), &(timers->recvtime))); print_message_line(received); } delays.retryAfter = timers->timer_t2; cdata.dontsend=1; return; } else { /* anything else then 483 or provisional will be treated as final */ printf("%i: ", namebeg); warning_extract(received); printf("(%.3f ms) ", deltaT(&(timers->sendtime), &(timers->recvtime))); print_message_line(received); if ((contact = STRCASESTR(received, CONT_STR)) != NULL || (contact = STRCASESTR(received, CONT_SHORT_STR)) != NULL) { if (*contact == '\n') { contact++; } printf("\t"); print_message_line(contact); } else { printf("\twithout Contact header\n"); } if (regexec(&(regexps.okexp), received, 0, 0, 0) == REG_NOERROR) { on_success(received, regex); } else { log_message(request); exit_code(1, __PRETTY_FUNCTION__, "received final non-2xx reply"); } } } /* takes care of replies in the default mode */ void handle_default(regex_t *regex, struct sipsak_sr_time *timers) { /* in the normal send and reply case anything other then 1xx will be treated as final response*/ if (regexec(&(regexps.proexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 1) { printf("%s\n\n", received); printf("** reply received "); if ((counters.send_counter == 1) || (STRNCASECMP(request, ACK_STR, ACK_STR_LEN) == 0)) { printf("after %.3f ms **\n", deltaT(&(timers->firstsendt), &(timers->recvtime))); } else { printf("%.3f ms after first send\n and " "%.3f ms after last send **\n", deltaT(&(timers->firstsendt), &(timers->recvtime)), deltaT(&(timers->sendtime), &(timers->recvtime))); } printf(" "); print_message_line(received); printf(" provisional received; still" " waiting for a final response\n"); } if (inv_trans) { delays.retryAfter = timers->timer_final; } else { delays.retryAfter = timers->timer_t2; } cdata.dontsend = 1; return; } else { if (verbose > 1) { printf("%s\n\n", received); printf("** reply received "); if ((counters.send_counter == 1) || (STRNCASECMP(request, ACK_STR, ACK_STR_LEN) == 0)){ printf("after %.3f ms **\n", deltaT(&(timers->firstsendt), &(timers->recvtime))); } else { printf("%.3f ms after first send\n and " "%.3f ms after last send **\n", deltaT(&(timers->firstsendt), &(timers->recvtime)), deltaT(&(timers->sendtime), &(timers->recvtime))); } printf(" "); print_message_line(received); printf(" final received\n"); } else if (verbose>0) { printf("%s\n", received); } if (timers->timing > 0) { timers->timing--; if (timers->timing == 0) { if (counters.run == 0) { counters.run++; } printf("%.3f/%.3f/%.3f ms\n", delays.small_delay, delays.all_delay / counters.run, delays.big_delay); } else { counters.run++; msg_data.cseq_counter = new_transaction(request, response); delays.retryAfter = timers->timer_t1; } } if (timers->timing == 0) { if (regexec(&(regexps.okexp), received, 0, 0, 0) == REG_NOERROR) { on_success(received, regex); } else { log_message(request); exit_code(1, __PRETTY_FUNCTION__, "received final non-2xx reply"); } } } } /* takes care of replies in the readntrash mode */ void handle_randtrash(int warning_ext, int nameend) { /* in randomzing trash we are expexting 4?? error codes everything else should not be normal */ if (regexec(&(regexps.errexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 2) printf("received:\n%s\n", received); if (verbose > 1) { printf("received expected 4xx "); if (warning_ext == 1) { printf ("from "); warning_extract(received); printf("\n"); } else { printf("\n"); } } } else { fprintf(stderr, "warning: did not received 4xx\n"); if (verbose > 1) printf("sended:\n%s\nreceived:\n%s\n", request, received); } if (msg_data.cseq_counter == nameend) { if (counters.randretrys == 0) { printf("random end reached. server survived :) respect!\n"); exit_code(0, __PRETTY_FUNCTION__, NULL); } else { printf("maximum sendings reached but did not " "get a response on this request:\n%s\n", request); log_message(request); exit_code(3, __PRETTY_FUNCTION__, "missing reply on trashed request"); } } else { trash_random(request); } } /* takes care of replies in the usrloc mode */ void handle_usrloc(regex_t *regex, int namebeg, int nameend, int rand_rem, char *username, int nagios_warn, struct sipsak_sr_time *timers, char *mes_body, enum sipsak_modes mode) { char *crlf; char ruri[11+12+20]; //FIXME: username length 20 should be dynamic if (regexec(&(regexps.proexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 2) { print_message_line(received); printf("ignoring provisional response\n\n"); } if (inv_trans) { delays.retryAfter = timers->timer_final; } else { delays.retryAfter = timers->timer_t2; } cdata.dontsend = 1; } else { switch (usrlocstep) { case REG_REP: /* we have sent a register and look at the response now */ if (regexec(&(regexps.okexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 1) { printf ("\tOK\n"); } if (verbose > 2) { printf("\n%s\n", received); } } else { fprintf(stderr, "received:\n%s\nerror: didn't " "received '200 OK' on register (see " "above). aborting\n", received); log_message(request); exit_code(1, __PRETTY_FUNCTION__, "received non-2xx reply for REGISTER"); } if (mode == SM_USRLOC) { if (namebeg==nameend) { if (verbose>0) { printf("\nAll usrloc tests" " completed successful.\nreceived" " last message %.3f ms after first" " request (test duration).\n", deltaT(&(timers->firstsendt), &(timers->recvtime))); } if (delays.big_delay>0 && verbose>0) { printf("biggest delay between " "request and response was %.3f" " ms\n", delays.big_delay); } if (counters.retrans_r_c>0 && verbose>0) { printf("%i retransmission(s) received from server.\n", counters.retrans_r_c); } if (counters.retrans_s_c>0 && verbose>0) { printf("%i time(s) the timeout of " "%i ms exceeded and request was" " retransmitted.\n", counters.retrans_s_c, delays.retryAfter); if (counters.retrans_s_c > nagios_warn) { log_message(request); exit_code(4, __PRETTY_FUNCTION__, "#retransmissions above nagios warn level"); } } if (timers->timing) { printf("%.3f ms\n", deltaT(&(timers->firstsendt), &(timers->recvtime))); } on_success(received, regex); } /* namebeg == nameend */ /* lets see if we deceid to remove a binding (case 6)*/ if ( ((float)rand()/RAND_MAX)*100 > rand_rem) { // FIXME we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_usern(usern, username, namebeg); create_msg(REQ_REG, &msg_data); } else { /* to prevent only removing of low user numbers new random number*/ msg_data.cseq_counter++; create_usern(usern, username, ((float)rand()/RAND_MAX) * namebeg); create_msg(REQ_REM, &msg_data); usrlocstep=UNREG_REP; } } /* invite == 0 && message == 0 */ else if (mode == SM_USRLOC_INVITE) { msg_data.cseq_counter++; create_msg(REQ_INV, &msg_data); inv_trans = 1; usrlocstep=INV_RECV; } else if (mode == SM_USRLOC_MESSAGE) { msg_data.cseq_counter++; create_msg(REQ_MES, &msg_data); inv_trans = 0; usrlocstep=MES_RECV; } break; case INV_RECV: /* see if we received our invite */ sprintf(ruri, "%s sip:%s", INV_STR, usern); if (!STRNCASECMP(received, ruri, strlen(ruri))) { if (verbose > 1) { printf("\t\treceived invite\n"); } if (verbose > 2) { printf("\n%s\n", received); } cpy_vias(received, response); cpy_rr(received, response, 0); swap_ptr(&request, &response); usrlocstep=INV_OK_RECV; inv_trans = 0; } else { fprintf(stderr, "received:\n%s\nerror: did not " "received the INVITE that was sent " "(see above). aborting\n", received); log_message(request); exit_code(1, __PRETTY_FUNCTION__, "did not received our own INVITE request"); } break; case INV_OK_RECV: /* did we received our ok ? */ if (STRNCASECMP(received, INV_STR, INV_STR_LEN)==0) { if (verbose>0) { printf("ignoring INVITE retransmission\n"); } counters.retrans_r_c++; cdata.dontsend=1; return; } if (regexec(&(regexps.okexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 1) { printf("\t200 OK received\n"); } if (verbose > 2) { printf("\n%s\n", received); } /* ACK was send already earlier generically */ usrlocstep=INV_ACK_RECV; cdata.dontsend=1; } else { fprintf(stderr, "received:\n%s\nerror: did not " "receive the '200 OK' that was sent " "as the reply on the INVITE (see " "above). aborting\n", received); log_message(request); exit_code(1, __PRETTY_FUNCTION__, "did not receive our own 200 reply"); } break; case INV_ACK_RECV: /* did we received our ack */ if (STRNCASECMP(received, SIP200_STR, SIP200_STR_LEN)==0) { if (verbose>0) { printf("ignoring 200 OK retransmission\n"); } counters.retrans_r_c++; cdata.dontsend=1; return; } sprintf(ruri, "%s sip:sipsak_conf@", ACK_STR); if (STRNCASECMP(received, ruri, strlen(ruri))==0) { if (verbose > 1) { printf("\tACK received\n"); } if (verbose > 2) { printf("\n%s\n", received); } if (verbose>0 && nameend>0) { printf("usrloc for %s%i completed " "successful\n", username, namebeg); } else if (verbose>0) { printf("usrloc for %s completed successful\n", username); } if (namebeg==nameend) { if (verbose>0) { printf("\nAll usrloc tests completed " "successful.\nreceived last message" " %.3f ms after first request (test" " duration).\n", deltaT(&(timers->firstsendt), &(timers->recvtime))); } if (delays.big_delay>0) { printf("biggest delay between " "request and response was %.3f" " ms\n", delays.big_delay); } if (counters.retrans_r_c>0) { printf("%i retransmission(s) received from server.\n", counters.retrans_r_c); } if (counters.retrans_s_c>0) { printf("%i time(s) the timeout of " "%i ms exceeded and request was" " retransmitted.\n", counters.retrans_s_c, delays.retryAfter); if (counters.retrans_s_c > nagios_warn) { log_message(request); exit_code(4, __PRETTY_FUNCTION__, "#retransmissions above nagios warn level"); } } on_success(received, regex); } /* namebeg == nameend */ if (mode == SM_USRLOC_INVITE) { /* lets see if we deceid to remove a binding (case 6)*/ if (((float)rand()/RAND_MAX) * 100 > rand_rem) { // FIXME we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_usern(usern, username, namebeg); create_msg(REQ_REG, &msg_data); usrlocstep=REG_REP; } else { /* to prevent only removing of low user numbers new random number*/ msg_data.cseq_counter++; create_usern(usern, username, ((float)rand()/RAND_MAX) * namebeg); create_msg(REQ_REM, &msg_data); usrlocstep=UNREG_REP; } } /* usrloc == 1 */ else { // FIXME we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_usern(usern, username, namebeg); create_msg(REQ_INV, &msg_data); inv_trans = 1; usrlocstep=INV_RECV; } } /* STRNCASECMP */ else { fprintf(stderr, "received:\n%s\nerror: did not " "receive the 'ACK' that was sent " "as the reply on the '200 OK' (see " "above). aborting\n", received); log_message(request); exit_code(1, __PRETTY_FUNCTION__, "missing ACK that was sent by myself"); } break; case MES_RECV: /* we sent the message and look if its forwarded to us */ sprintf(ruri, "%s sip:%s", MES_STR, usern); if (!STRNCASECMP(received, ruri, strlen(ruri))) { if (verbose > 1) { crlf=STRCASESTR(received, "\r\n\r\n"); crlf=crlf+4; printf(" received message\n '%s'\n", crlf); } if (verbose > 2) { printf("\n%s\n", received); } cpy_vias(received, response); swap_ptr(&request, &response); usrlocstep=MES_OK_RECV; } else { fprintf(stderr, "received:\n%s\nerror: did not " "receive the 'MESSAGE' that was sent " "(see above). aborting\n", received); log_message(request); exit_code(1, __PRETTY_FUNCTION__, "did not receive my own MESSAGE request"); } break; case MES_OK_RECV: /* we sent our reply on the message and look if this is also forwarded to us */ if (STRNCASECMP(received, MES_STR, MES_STR_LEN)==0) { if (verbose>0) { printf("ignoring MESSAGE retransmission\n"); } counters.retrans_r_c++; cdata.dontsend=1; return; } if (regexec(&(regexps.okexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 1) { printf(" reply received\n\n"); } else if (verbose>0 && nameend>0) { printf("usrloc for %s%i completed " "successful\n", username, namebeg); } else if (verbose>0) { printf("usrloc for %s completed successful\n", username); } if (namebeg==nameend) { if (verbose>0) { printf("\nAll usrloc tests completed " "successful.\nreceived last message" " %.3f ms after first request (test" " duration).\n", deltaT(&(timers->firstsendt), &(timers->recvtime))); } if (delays.big_delay>0) { printf("biggest delay between " "request and response was %.3f" " ms\n", delays.big_delay); } if (counters.retrans_r_c>0) { printf("%i retransmission(s) " "received from server.\n", counters.retrans_r_c); } if (counters.retrans_s_c>0) { printf("%i time(s) the timeout of " "%i ms exceeded and request was" " retransmitted.\n", counters.retrans_s_c, delays.retryAfter); if (counters.retrans_s_c > nagios_warn) { log_message(request); exit_code(4, __PRETTY_FUNCTION__, "#retransmissions above nagios warn level"); } } on_success(received, regex); } /* namebeg == nameend */ if (mode == SM_USRLOC_MESSAGE) { /* lets see if we deceid to remove a binding (case 6)*/ if (((float)rand()/RAND_MAX) * 100 > rand_rem) { // FIXME we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_usern(usern, username, namebeg); create_msg(REQ_REG, &msg_data); usrlocstep=REG_REP; } else { /* to prevent only removing of low user numbers new random number*/ msg_data.cseq_counter++; create_usern(usern, username, ((float)rand()/RAND_MAX) * namebeg); create_msg(REQ_REM, &msg_data); usrlocstep=UNREG_REP; } } /* usrloc == 1 */ else { // FIXME we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_usern(usern, username, namebeg); create_msg(REQ_MES, &msg_data); usrlocstep=MES_RECV; } } /* regexec */ else { if (verbose>0) { if (mes_body) { fprintf(stderr, "received:\n%s\nerror: did" " not receive 200 for the " "MESSAGE (see above)\n", received); } else { fprintf(stderr, "received:\n%s\nerror: did" " not receive the '200 OK' " "that was sent as the reply on" " the MESSAGE (see above). " "aborting\n", received); } } log_message(request); exit_code(1, __PRETTY_FUNCTION__, "received non-2xx reply for MESSAGE request"); } break; case UNREG_REP: if (STRNCASECMP(received, MES_STR, MES_STR_LEN)==0) { if (verbose>0) { printf("ignoring MESSAGE retransmission\n"); } counters.retrans_r_c++; cdata.dontsend=1; return; } if (regexec(&(regexps.okexp), received, 0, 0, 0) == REG_NOERROR) { if (verbose > 1) { printf(" OK\n\n"); } else if (verbose>0 && nameend>0) { printf("Binding removal for %s%i " "successful\n", username, namebeg); } else if (verbose>0) { printf("Binding removal for %s successful\n", username); } // FIXME we want to modify the global value here namebeg++; msg_data.cseq_counter++; create_usern(usern, username, namebeg); create_msg(REQ_REG, &msg_data); usrlocstep=REG_REP; } else { fprintf(stderr, "received:\n%s\nerror: did not " "receive the expected 200 on the " "remove bindings request for %s%i (see" " above). aborting\n", received, username, namebeg); log_message(request); exit_code(1, __PRETTY_FUNCTION__, "received non-2xx reply for de-register request"); } break; default: fprintf(stderr, "error: unknown step in usrloc\n"); exit_code(2, __PRETTY_FUNCTION__, "unknown step in usrloc"); break; } /* switch */ } /* regexec proexp */ } void before_sending(struct sipsak_counter *counter, struct sipsak_msg_data *msg_data, enum sipsak_modes mode) { /* some initial output */ if ((mode == SM_USRLOC || mode == SM_USRLOC_INVITE || mode == SM_USRLOC_MESSAGE || mode == SM_INVITE || mode == SM_MESSAGE) && (verbose > 1) && (cdata.dontsend == 0)) { switch (usrlocstep) { case REG_REP: if (counter->nameend>0) printf("registering user %s%i... ", msg_data->username, counter->namebeg); else printf("registering user %s... ", msg_data->username); break; case INV_RECV: if (counter->nameend>0) printf("inviting user %s%i... ", msg_data->username, counter->namebeg); else printf("inviting user %s... ", msg_data->username); break; case INV_OK_RECV: printf("sending invite reply... "); break; case INV_ACK_RECV: printf("sending invite ack... "); break; case MES_RECV: if (counter->nameend>0) printf("sending message to %s%i... ", msg_data->username, counter->namebeg); else printf("sending message to %s... ", msg_data->username); break; case MES_OK_RECV: if (msg_data->mes_body) printf("sending message ... \n"); else printf("sending message reply... "); break; case UNREG_REP: if (counter->nameend>0) printf("remove binding for %s%i...", msg_data->username, counter->namebeg); else printf("remove binding for %s...", msg_data->username); break; } } /* if usrloc...*/ else if (mode == SM_FLOOD && verbose > 0) { printf("flooding message number %i\n", counter->namebeg); } else if (mode == SM_RANDTRASH && verbose > 0) { printf("message with %i randomized chars\n", msg_data->cseq_counter); if (verbose > 2) printf("request:\n%s\n", request); } } /* this is the main function with the loops and modes */ void shoot(char *buf, int buff_size, struct sipsak_options *options) { struct timespec sleep_ms_s, sleep_rem; int ret, cseqtmp, rand_tmp; char buf2[BUFSIZE], buf3[BUFSIZE], lport_str[LPORT_STR_LEN]; inv_trans = 0; usrlocstep = REG_REP; /* initalize local vars */ cdata.dontsend=cdata.dontrecv=counters.retrans_r_c=counters.retrans_s_c= 0; delays.big_delay=counters.send_counter=counters.run= 0; usern = NULL; /* initialize local arrays */ memset(buf2, 0, BUFSIZE); memset(buf3, 0, BUFSIZE); memset(lport_str, 0, LPORT_STR_LEN); memset(fqdn, 0, FQDN_SIZE); /* initialize external vars which don't have initializer */ nonce_count = 0; counters.namebeg = options->namebeg; counters.nameend = options->nameend; cdata.csock = cdata.usock = -1; cdata.connected = 0; cdata.transport = options->transport; cdata.symmetric = options->symmetric; cdata.lport = options->lport; cdata.rport = options->rport; cdata.buf_tmp = NULL; cdata.buf_tmp_size = 0; memset(&(timers.sendtime), 0, sizeof(timers.sendtime)); memset(&(timers.recvtime), 0, sizeof(timers.recvtime)); memset(&(timers.firstsendt), 0, sizeof(timers.firstsendt)); memset(&(timers.starttime), 0, sizeof(timers.starttime)); memset(&(timers.delaytime), 0, sizeof(timers.delaytime)); timers.timer_t1 = options->timer_t1; timers.timer_t2 = 8 * timers.timer_t1; timers.timer_final = options->timer_final * timers.timer_t1; timers.timing = options->timing; /* delays.retryAfter = DEFAULT_TIMEOUT; */ if (cdata.transport == SIP_UDP_TRANSPORT) { delays.retryAfter = timers.timer_t1; } else { delays.retryAfter = timers.timer_final; } request = buf; response = buf2; received = buf3; msg_data.cseq_counter = 1; msg_data.lport = cdata.lport; msg_data.expires_t = options->expires_t; msg_data.empty_contact = options->empty_contact; msg_data.transport = options->transport; msg_data.req_buff = request; msg_data.repl_buff = NULL; msg_data.username = options->username; msg_data.domainname = options->domainname; msg_data.contact_uri = options->contact_uri; msg_data.con_dis = options->con_dis; msg_data.from_uri = options->from_uri; msg_data.mes_body = options->mes_body; msg_data.headers = options->headers; msg_data.fqdn = fqdn; init_network(&cdata, options->local_ip #ifdef WITH_TLS_TRANSP , options->ca_file #endif ); if (msg_data.lport == 0) { msg_data.lport = cdata.lport; } /* determine our hostname */ get_fqdn(&fqdn[0], options->numeric, options->hostname); if (options->replace_b == 1){ replace_string(request, "$dsthost$", options->domainname); replace_string(request, "$srchost$", fqdn); sprintf(lport_str, "%i", cdata.lport); replace_string(request, "$port$", lport_str); if (msg_data.username) replace_string(request, "$user$", msg_data.username); } if (options->replace_str) replace_strings(request, options->replace_str); /* set all regular expression to simplfy the result code identification */ regcomp(&(regexps.replyexp), "^SIP/[0-9]\\.[0-9] [1-6][0-9][0-9]", REG_EXTENDED|REG_NOSUB|REG_ICASE); regcomp(&(regexps.proexp), "^SIP/[0-9]\\.[0-9] 1[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); regcomp(&(regexps.okexp), "^SIP/[0-9]\\.[0-9] 2[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); regcomp(&(regexps.redexp), "^SIP/[0-9]\\.[0-9] 3[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); regcomp(&(regexps.authexp), "^SIP/[0-9]\\.[0-9] 40[17] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); regcomp(&(regexps.errexp), "^SIP/[0-9]\\.[0-9] 4[0-9][0-9] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); regcomp(&(regexps.tmhexp), "^SIP/[0-9]\\.[0-9] 483 ", REG_EXTENDED|REG_NOSUB|REG_ICASE); if (msg_data.username) { if (counters.nameend > 0) { usern = str_alloc(strlen(msg_data.username) + 12); create_usern(usern, msg_data.username, counters.namebeg); msg_data.username = usern; } else { if (*(msg_data.username + strlen(msg_data.username) - 1) != '@') { usern = str_alloc(strlen(msg_data.username) + 2); create_usern(usern, msg_data.username, -1); msg_data.username = usern; } else { usern = msg_data.username; } } } /* calculate the number of required steps and create initial mes */ if (options->mode == SM_USRLOC || options->mode == SM_USRLOC_INVITE || options->mode == SM_USRLOC_MESSAGE) { create_msg(REQ_REG, &msg_data); usrlocstep=REG_REP; } else if (options->mode == SM_INVITE) { create_msg(REQ_INV, &msg_data); inv_trans = 1; usrlocstep=INV_RECV; } else if (options->mode == SM_MESSAGE) { create_msg(REQ_MES, &msg_data); if (msg_data.mes_body) usrlocstep=MES_OK_RECV; else usrlocstep=MES_RECV; } else if (options->mode == SM_TRACE){ /* for trace we need some spezial initis */ counters.namebeg=0; create_msg(REQ_OPT, &msg_data); set_maxforw(request, counters.namebeg); } else if (options->mode == SM_FLOOD){ if (counters.nameend<=0) { counters.nameend=INT_MAX; } counters.namebeg=1; create_msg(REQ_FLOOD, &msg_data); } else if (options->mode == SM_RANDTRASH){ counters.randretrys=0; counters.namebeg=1; create_msg(REQ_RAND, &msg_data); counters.nameend=(int)strlen(request); if (options->trashchar == 1){ if (options->trashchar < counters.nameend) counters.nameend=options->trashchar; else fprintf(stderr, "warning: number of trashed chars to big. setting to " "request length\n"); } trash_random(request); } else { /* for none of the modes we also need some inits */ if (options->file_b == 0) { counters.namebeg=1; create_msg(REQ_OPT, &msg_data); } else { if (STRNCASECMP(request, INV_STR, INV_STR_LEN) == 0) { inv_trans = 1; } if(options->via_ins == 1) add_via(request, msg_data.fqdn, msg_data.lport); } /* delays.retryAfter = delays.retryAfter / 10; */ if(options->maxforw != -1) set_maxforw(request, options->maxforw); } cdata.connected = set_target(&(cdata.adr), options->address, cdata.rport, cdata.csock, cdata.connected, cdata.transport, options->domainname #ifdef WITH_TLS_TRANSP , options->ignore_ca_fail #endif ); /* here we go until someone decides to exit */ while(1) { before_sending(&counters, &msg_data, options->mode); if (options->sleep_ms == -2) { rand_tmp = rand(); sleep_ms_s.tv_sec = rand_tmp / 1000; sleep_ms_s.tv_nsec = (rand_tmp % 1000) * 1000; } else if (options->sleep_ms != 0) { sleep_ms_s.tv_sec = options->sleep_ms; sleep_ms_s.tv_nsec = (options->sleep_ms % 1000) * 1000000; } if (options->sleep_ms != 0) { dbg("sleeping for %li s + %li ns\n", sleep_ms_s.tv_sec, sleep_ms_s.tv_nsec); nanosleep(&sleep_ms_s, &sleep_rem); } send_message(request, &cdata, &counters, &timers); /* in flood we are only interested in sending so skip the rest */ if (options->mode != SM_FLOOD) { ret = recv_message(received, BUFSIZE, inv_trans, &delays, &timers, &counters, &cdata, ®exps, options->mode, msg_data.cseq_counter, request, response); if(ret > 0) { if (usrlocstep == INV_OK_RECV) { swap_ptr(&response, &request); } /* send ACK for non-provisional reply on INVITE */ if ((STRNCASECMP(request, "INVITE", 6)==0) && (regexec(&(regexps.replyexp), received, 0, 0, 0) == REG_NOERROR) && (regexec(&(regexps.proexp), received, 0, 0, 0) == REG_NOMATCH)) { build_ack(request, received, response, ®exps); cdata.dontsend = 0; inv_trans = 0; /* lets fire the ACK to the server */ send_message(response, &cdata, &counters, &timers); inv_trans = 1; } /* check for old CSeq => ignore retransmission */ cseqtmp = cseq(received); if ((0 < cseqtmp) && (cseqtmp < msg_data.cseq_counter)) { if (verbose>0) { printf("ignoring retransmission\n"); } counters.retrans_r_c++; cdata.dontsend = 1; continue; } else if (regexec(&(regexps.authexp), received, 0, 0, 0) == REG_NOERROR) { if (!msg_data.username && !options->auth_username) { if (timers.timing > 0) { timers.timing--; if (timers.timing == 0) { if (counters.run == 0) { counters.run++; } printf("%.3f/%.3f/%.3f ms\n", delays.small_delay, delays.all_delay / counters.run, delays.big_delay); exit_code(0, __PRETTY_FUNCTION__, NULL); } counters.run++; msg_data.cseq_counter = new_transaction(request, response); delays.retryAfter = timers.timer_t1; continue; } fprintf(stderr, "%s\nerror: received 40[17] but cannot " "authentication without a username or auth username\n", received); log_message(request); exit_code(2, __PRETTY_FUNCTION__, "missing username for authentication"); } /* prevents a strange error */ regcomp(&(regexps.authexp), "^SIP/[0-9]\\.[0-9] 40[17] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); insert_auth(request, received, msg_data.username, options->password, options->auth_username, options->authhash, counters.namebeg, counters.nameend); if (verbose > 2) printf("\nreceived:\n%s\n", received); msg_data.cseq_counter = new_transaction(request, response); continue; } /* if auth...*/ /* lets see if received a redirect */ if (options->redirects == 1 && regexec(&(regexps.redexp), received, 0, 0, 0) == REG_NOERROR) { handle_3xx(&(cdata.adr), options->warning_ext, cdata.rport, options->address, cdata.transport, options->outbound_proxy, options->domainname #ifdef WITH_TLS_TRANSP , options->ignore_ca_fail #endif ); } /* if redircts... */ else if (options->mode == SM_TRACE) { trace_reply(options->regex, counters.namebeg, &timers); } /* if trace ... */ else if (options->mode == SM_USRLOC || options->mode == SM_USRLOC_INVITE || options->mode == SM_USRLOC_MESSAGE || options->mode == SM_INVITE || options->mode == SM_MESSAGE) { handle_usrloc(options->regex, counters.namebeg, counters.nameend, options->rand_rem, msg_data.username, options->nagios_warn, &timers, msg_data.mes_body, options->mode); } else if (options->mode == SM_RANDTRASH) { handle_randtrash(options->warning_ext, counters.nameend); } else { handle_default(options->regex, &timers); } /* redirect, auth, and modes */ } /* ret > 0 */ else if (ret == -1) { // we did not got anything back, send again /* no re-transmission on reliable transports */ if (cdata.transport != SIP_UDP_TRANSPORT) { cdata.dontsend = 1; } continue; } else if (ret == -2) { // we received non-matching ICMP cdata.dontsend = 1; continue; } else { if (options->mode == SM_USRLOC || options->mode == SM_USRLOC_INVITE || options->mode == SM_USRLOC_MESSAGE) { printf("failed\n"); } perror("socket error"); exit_code(3, __PRETTY_FUNCTION__, "internal socket error"); } } /* !flood */ else { if (counters.send_counter == 1) { memcpy(&(timers.firstsendt), &(timers.sendtime), sizeof(struct timeval)); } if (counters.namebeg==counters.nameend) { printf("flood end reached\n"); printf("it took %.3f ms seconds to send %i request.\n", deltaT(&(timers.firstsendt), &(timers.sendtime)), counters.namebeg); printf("we sent %f requests per second.\n", (counters.namebeg/(deltaT(&(timers.firstsendt), &(timers.sendtime)))*1000)); exit_code(0, __PRETTY_FUNCTION__, NULL); } counters.namebeg++; msg_data.cseq_counter++; create_msg(REQ_FLOOD, &msg_data); } } /* while 1 */ /* this should never happen any more... */ if (options->mode == SM_RANDTRASH) { exit_code(0, __PRETTY_FUNCTION__, NULL); } printf("** give up further retransmissions....\n"); if (counters.retrans_r_c>0 && (verbose > 1)) { printf("%i retransmissions received during test\n", counters.retrans_r_c); } if (counters.retrans_s_c>0 && (verbose > 1)) { printf("sent %i retransmissions during test\n", counters.retrans_s_c); } exit_code(3, __PRETTY_FUNCTION__, "got outside of endless messaging loop"); } sipsak-0.9.8.1/src/shoot.h000066400000000000000000000021041377576723000152730ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_SHOOT_H #define SIPSAK_SHOOT_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #define LPORT_STR_LEN 6 struct sipsak_regexp { regex_t redexp; regex_t proexp; regex_t okexp; regex_t tmhexp; regex_t errexp; regex_t authexp; regex_t replyexp; }; enum usteps { REG_REP, INV_RECV, INV_OK_RECV, INV_ACK_RECV, MES_RECV, MES_OK_RECV, UNREG_REP }; void shoot(char *buff, int buff_size, struct sipsak_options *options); #endif sipsak-0.9.8.1/src/sipsak.c000066400000000000000000001052441377576723000154350ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* sipsak written by nils ohlmeier (ohlmeier@fokus.fraunhofer.de). based up on a modifyed version of shoot. return codes are now: 0 for an received 200, 1 for all other received responses, 2 for local errors, and 3 for remote errors. */ /* changes by jiri@iptel.org; now messages can be really received; status code returned is 2 for some local errors , 0 for success and 1 for remote error -- ICMP/timeout; can be used to test if a server is alive; 1xx messages are now ignored; windows support dropped */ #include "sipsak.h" #ifdef HAVE_UNISTD_H # ifdef HAVE_SYS_TYPES_H # include # endif # include #endif #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_GETOPT_H # include #endif #ifdef HAVE_SYSLOG_H # include #endif #include "helper.h" #include "header_f.h" #include "shoot.h" #include "exit_code.h" int verbose; static void sigchld_handler(int signo) { int chld_status; pid_t chld; while ((chld = waitpid(-1, &chld_status, WNOHANG)) > 0); } void print_version() { printf("%s %s by Nils Ohlmeier\n", PACKAGE_NAME, PACKAGE_VERSION); printf(" Copyright (C) 2002-2004 FhG Fokus\n"); printf(" Copyright (C) 2004-2005 Nils Ohlmeier\n"); printf(" report bugs to %s\n\n", PACKAGE_BUGREPORT); printf( " shoot : sipsak [-f FILE] [-L] -s SIPURI\n" " trace : sipsak -T -s SIPURI\n" " usrloc : sipsak -U [-I|M] [-b NUMBER] [-e NUMBER] [-x NUMBER] [-z NUMBER] -s SIPURI\n" " usrloc : sipsak -I|M [-b NUMBER] [-e NUMBER] -s SIPURI\n" " usrloc : sipsak -U [-C SIPURI] [-x NUMBER] -s SIPURI\n" " message: sipsak -M [-B STRING] [-O STRING] [-c SIPURI] -s SIPURI\n" " flood : sipsak -F [-e NUMBER] -s SIPURI\n" " random : sipsak -R [-t NUMBER] -s SIPURI\n\n" " additional parameter in every mode:\n" ); printf(" [-a PASSWORD] [-d] [-i] [-H HOSTNAME] [-l PORT] [-m NUMBER] [-n] " "[-N]\n" " [-r PORT] [-v] [-V] [-w]\n\n" ); } #ifdef HAVE_GETOPT_LONG void print_long_help() { print_version(); printf( " --help displays this help message\n" " --version prints version string only\n" " --filename=FILE the file which contains the SIP message to send\n" " use - for standard input\n" " --no-crlf de-activate CR (\\r) insertion\n" " --sip-uri=SIPURI the destination server URI in form\n" " sip:[user@]servername[:port]\n" " --traceroute activates the traceroute mode\n" ); printf(" --usrloc-mode activates the usrloc mode\n" " --invite-mode simulates a successful calls with itself\n" " --message-mode sends messages to itself\n" " --contact=SIPURI use the given URI as Contact in REGISTER\n" " --appendix-begin=NUMBER the starting number appendix to the user name (default: 0)\n" " --appendix-end=NUMBER the ending number of the appendix to the user name\n" " --sleep=NUMBER sleep number ms before sending next request\n" ); printf(" --expires=NUMBER the expires header field value (default: 15)\n" " --remove-bindings=NUMBER activates randomly removing of user bindings\n" " --flood-mode activates the flood mode\n" " --random-mode activates the random mode (dangerous)\n" " --trash-chars=NUMBER the maximum number of trashed character in random mode\n" " (default: request length)\n" ); printf(" --local-port=PORT the local port to use (default: any)\n" " --remote-port=PORT the remote port to use (default: 5060)\n" " --outbound-proxy=HOSTNAME request target (outbound proxy)\n" " --hostname=HOSTNAME overwrites the local hostname in all headers\n" " --max-forwards=NUMBER the value for the max-forwards header field\n" #ifdef OLDSTYLE_FQDN " --numeric use IP instead of FQDN in the Via-Line\n" #else " --numeric use FQDN instead of IP in the Via-Line\n" #endif ); printf(" --processes=NUMBER Divide the workflow among the number of processes\n" " --auth-username=STRING username for authentication\n" ); printf(" --no-via deactivate the insertion of a Via-Line\n" " --password=PASSWORD password for authentication\n" " (if omitted password=username)\n" " --ignore-redirects ignore redirects\n" " --verbose each v produces more verbosity (max. 3)\n" " --extract-ip extract IP from the warning in reply\n" " --replace-string=STRING replacement for a special mark in the message\n" " --replace activates replacement of variables\n" ); printf(" --nagios-code returns exit codes Nagios compliant\n" " --nagios-warn=NUMBER return Nagios warning if retrans > number\n" " --message-body=STRING send a message with string as body\n" " --disposition=STRING Content-Disposition value\n" " --search=REGEXP search for a RegExp in replies and return error\n" " on failure\n" " --timing=NUMBER number of test runs and print just the timings\n" " --symmetric send and received on the same port\n" " --from=SIPURI use the given URI as From in MESSAGE\n" " --timeout-factor=NUMBER timeout multiplier for INVITE transactions\n" " on non-reliable transports (default: 64)\n" " --timer-t1=NUMBER timeout T1 in ms (default: %i)\n" " --transport=STRING specify transport to be used\n" " --headers=STRING adds additional headers to the request\n" " --local-ip=STRING specify local ip address to be used\n" " --authhash=STRING HA1 hash for authentication instead of password\n" " --syslog=NUMBER log exit message to syslog with given log level\n" , DEFAULT_TIMEOUT ); #ifdef WITH_TLS_TRANSP printf(" --tls-ca-cert=FILE file with the cert of the root CA\n" " --tls-client-cert=FILE file with the cert which sipsak will send\n" " --tls-ignore-cert-failure ignore failures during the TLS handshake\n" ); #endif exit_code(0, __PRETTY_FUNCTION__, NULL); } #endif /* prints out some usage help and exits */ void print_help() { print_version(); printf( " -h displays this help message\n" " -V prints version string only\n" " -f FILE the file which contains the SIP message to send\n" " use - for standard input\n" " -L de-activate CR (\\r) insertion in files\n" " -s SIPURI the destination server URI in form\n" " sip:[user@]servername[:port]\n" " -T activates the traceroute mode\n" " -U activates the usrloc mode\n" " -I simulates a successful calls with itself\n" " -M sends messages to itself\n" ); printf( " -C SIPURI use the given URI as Contact in REGISTER\n" " -b NUMBER the starting number appendix to the user name (default: 0)\n" " -e NUMBER the ending number of the appendix to the user name\n" " -o NUMBER sleep number ms before sending next request\n" " -x NUMBER the expires header field value (default: 15)\n" " -z NUMBER activates randomly removing of user bindings\n" " -F activates the flood mode\n" ); printf( " -R activates the random mode (dangerous)\n" " -t NUMBER the maximum number of trashed character in random mode\n" " (default: request length)\n" " -l PORT the local port to use (default: any)\n" " -r PORT the remote port to use (default: 5060)\n" " -p HOSTNAME request target (outbound proxy)\n" ); printf( " -H HOSTNAME overwrites the local hostname in all headers\n" " -m NUMBER the value for the max-forwards header field\n" #ifdef OLDSTYLE_FQDN " -n use IP instead of FQDN in the Via-Line\n" #else " -n use FQDN instead of IP in the Via-Line\n" #endif " -i deactivate the insertion of a Via-Line\n" " -a PASSWORD password for authentication\n" " (if omitted password=\"\")\n" " -u STRING Authentication username\n" ); printf( " -d ignore redirects\n" " -v each v produces more verbosity (max. 3)\n" " -w extract IP from the warning in reply\n" " -g STRING replacement for a special mark in the message\n" " -G activates replacement of variables\n" " -N returns exit codes Nagios compliant\n" " -q STRING search for a RegExp in replies and return error\n" " on failure\n"); printf(" -W NUMBER return Nagios warning if retrans > number\n" " -B STRING send a message with string as body\n" " -O STRING Content-Disposition value\n" " -P NUMBER Number of processes to start\n" " -A NUMBER number of test runs and print just timings\n" " -S use same port for receiving and sending\n" " -c SIPURI use the given URI as From in MESSAGE\n" " -D NUMBER timeout multiplier for INVITE transactions\n" " on non-reliable transports (default: 64)\n" " -Z NUMBER timeout T1 in ms (default: %i)\n" " -E STRING specify transport to be used\n" " -j STRING adds additional headers to the request\n" " -J STRING HA1 hash for authentication instead of password\n" " -k STRING specify local ip address to be used\n" " -K NUMBER log exit message to syslog with given log level\n" , DEFAULT_TIMEOUT ); exit_code(0, __PRETTY_FUNCTION__, NULL); } int main(int argc, char *argv[]) { FILE *pf; char buff[BUFSIZE]; int c, i, port; unsigned int tsp; char *scheme, *user, *host, *backup; pid_t pid; struct timespec ts; int upp; struct sipsak_options options; #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option l_opts[] = { {"help", 0, 0, 0}, {"version", 0, 0, 'V'}, {"filename", 1, 0, 'f'}, {"sip-uri", 1, 0, 's'}, {"traceroute-mode", 0, 0, 'T'}, {"usrloc-mode", 0, 0, 'U'}, {"invite-mode", 0, 0, 'I'}, {"message-mode", 0, 0, 'M'}, {"contact", 1, 0, 'C'}, {"appendix-begin", 1, 0, 'b'}, {"appendix-end", 1, 0, 'e'}, {"sleep", 1, 0, 'o'}, {"expires", 1, 0, 'x'}, {"remove-bindings", 1, 0, 'z'}, {"flood-mode", 0, 0, 'F'}, {"random-mode", 0, 0, 'R'}, {"trash-chars", 1, 0, 't'}, {"local-port", 1, 0, 'l'}, {"remote-port", 1, 0, 'r'}, {"outbound-proxy", 1, 0, 'p'}, {"hostname", 1, 0, 'H'}, {"max-fowards", 1, 0, 'm'}, {"numeric", 0, 0, 'n'}, {"no-via", 0, 0, 'i'}, {"password", 1, 0, 'a'}, {"ignore-redirects", 0, 0, 'd'}, {"verbose", 0, 0, 'v'}, {"extract-ip", 0, 0, 'w'}, {"replace-string", 0, 0, 'g'}, {"replace", 0, 0, 'G'}, {"nagios-code", 0, 0, 'N'}, {"nagios-warn", 1, 0, 'W'}, {"search", 1, 0, 'q'}, {"message-body", 1, 0, 'B'}, {"disposition", 1, 0, 'O'}, {"processes", 1, 0, 'P'}, {"auth-username", 1, 0, 'u'}, {"no-crlf", 0, 0, 'L'}, {"timing", 1, 0, 'A'}, {"symmetric", 0, 0, 'S'}, {"from", 1, 0, 'c'}, {"timeout-factor", 1, 0, 'D'}, {"timer-t1", 1, 0, 'Z'}, {"transport", 1, 0, 'E'}, {"headers", 1, 0, 'j'}, {"authhash", 1, 0, 'J'}, {"local-ip", 1, 0, 'k'}, {"syslog", 1, 0, 'K'}, #ifdef WITH_TLS_TRANSP {"tls-ca-cert", 1, 0, 0}, {"tls-client-cert", 1, 0, 0}, {"tls-ignore-cert-failure", 0, 0, 0}, #endif {0, 0, 0, 0} }; #endif /* some initialisation to be safe */ verbose = 0; memset(&options, 0, sizeof(struct sipsak_options)); options.namebeg = -1; options.nameend = -1; options.maxforw = -1; #ifdef OLDSTYLE_FQDN options.numeric = 0; #else options.numeric = 1; #endif options.via_ins = 1; options.redirects = 1; options.fix_crlf = 1; options.processes = 1; options.expires_t = USRLOC_EXP_DEF; options.timer_t1 = SIP_T1; options.timer_final = 64; tsp = 0; memset(buff, 0, BUFSIZE); if (argc==1) { print_help(); } /* lots of command line switches to handle*/ #ifdef HAVE_GETOPT_LONG while ((c=getopt_long(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:", l_opts, &option_index)) != EOF){ #else while ((c=getopt(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:")) != EOF){ #endif switch(c){ #ifdef HAVE_GETOPT_LONG case 0: printf("long option %s", l_opts[option_index].name); if (optarg) { printf(" with arg %s", optarg); } printf("\n"); if (STRNCASECMP("help", l_opts[option_index].name, 4) == 0) { print_long_help(); } #ifdef WITH_TLS_TRANSP else if (STRNCASECMP("tls-ca-cert", l_opts[option_index].name, 11) == 0) { pf = fopen(optarg, "rb"); if (!pf){ fprintf(stderr, "error: unable to open the CA cert file '%s'.\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "failed to open CA cert file"); } fclose(pf); options.ca_file=optarg; break; } else if (STRNCASECMP("tls-ignore-cert-failure", l_opts[option_index].name, 22) == 0) { options.ignore_ca_fail = 1; break; } else if (STRNCASECMP("tls-client-cert", l_opts[option_index].name, 14) == 0) { pf = fopen(optarg, "rb"); if (!pf){ fprintf(stderr, "error: unable to open the client cert file '%s'.\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "failed to open client cert file"); } fclose(pf); options.cert_file=optarg; break; } #endif break; #endif case 'a': if (strlen(optarg) == 1 && STRNCASECMP(optarg, "-", 1) == 0) { options.password = str_alloc(SIPSAK_MAX_PASSWD_LEN); printf("Please enter the password (max. length %i): ", SIPSAK_MAX_PASSWD_LEN); if (read_stdin(options.password, SIPSAK_MAX_PASSWD_LEN, 1) == 0) { exit_code(0, __PRETTY_FUNCTION__, NULL); } } else { options.password=str_alloc(strlen(optarg) + 1); strncpy(options.password, optarg, strlen(optarg)); } break; case 'A': options.timing=str_to_int(0, optarg); break; case 'b': options.namebeg=str_to_int(0, optarg); break; case 'B': options.mes_body=str_alloc(strlen(optarg) + 1); strncpy(options.mes_body, optarg, strlen(optarg)); break; case 'c': backup=str_alloc(strlen(optarg)+1); strncpy(backup, optarg, strlen(optarg)); parse_uri(backup, &scheme, &user, &host, &port); if (scheme == NULL) { fprintf(stderr, "error: missing scheme in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing scheme in From URI"); } else if (user == NULL) { fprintf(stderr, "error: missing username in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing username in From URI"); } else if (host == NULL) { fprintf(stderr, "error: missing host in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host in From URI"); } else { options.from_uri=str_alloc(strlen(optarg)+1); strncpy(options.from_uri, optarg, strlen(optarg)); } free(backup); break; case 'C': if ((strlen(optarg) == 5 && STRNCASECMP(optarg, "empty", 5) == 0) || (strlen(optarg) == 4 && STRNCASECMP(optarg, "none", 4) == 0)) { options.empty_contact = 1; } else if ((strlen(optarg) == 1 && STRNCASECMP(optarg, "*", 1) == 0) || (strlen(optarg) == 4 && STRNCASECMP(optarg, "star", 4) == 0)) { options.contact_uri=str_alloc(2); memcpy(options.contact_uri, "*", 1); } else { backup=str_alloc(strlen(optarg)+1); strncpy(backup, optarg, strlen(optarg)); parse_uri(backup, &scheme, &user, &host, &port); if (scheme == NULL) { fprintf(stderr, "error: REGISTER Contact URI doesn't not contain " "sip:, sips:, *, or is not empty\n"); exit_code(2, __PRETTY_FUNCTION__, "unsupported Contact for registration"); } /*else if (user == NULL) { fprintf(stderr, "error: missing username in Contact uri\n"); exit_code(2); }*/ else if (host == NULL) { fprintf(stderr, "error: missing host in Contact URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host in Contact"); } else { options.contact_uri=str_alloc(strlen(optarg)+1); strncpy(options.contact_uri, optarg, strlen(optarg)); } free(backup); } break; case 'd': options.redirects=0; break; case 'D': options.timer_final = str_to_int(0, optarg); if (options.timer_final <= 0) { fprintf(stderr, "error: option D has to be above 0\n"); exit_code(2, __PRETTY_FUNCTION__, "option D has to be above 0"); } break; case 'e': options.nameend=str_to_int(0, optarg); break; case 'E': if (strlen(optarg) == 3 && STRNCASECMP(optarg, "udp", 3) == 0) { options.transport = SIP_UDP_TRANSPORT; } else if (strlen(optarg) == 3 && STRNCASECMP(optarg, "tcp", 3) == 0) { options.transport = SIP_TCP_TRANSPORT; } #ifdef WITH_TLS_TRANSP else if (strlen(optarg) == 3 && STRNCASECMP(optarg, "tls", 3) == 0) { options.transport = SIP_TLS_TRANSPORT; } #endif else { fprintf(stderr, "error: unsupported transport '%s', supported values: udp, tcp\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "unsupported transport"); } break; case 'F': options.mode = SM_FLOOD; break; case 'f': if (strlen(optarg) != 1 && STRNCASECMP(optarg, "-", 1) != 0) { /* file is opened in binary mode so that the cr-lf is preserved */ pf = fopen(optarg, "rb"); if (!pf){ fprintf(stderr, "error: unable to open the file '%s'.\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "failed to open file from the f option"); } if (fread(buff, 1, sizeof(buff), pf) >= sizeof(buff)){ fprintf(stderr, "error:the file is too big. try files of less " "than %i bytes.\n", BUFSIZE); fprintf(stderr, " or recompile the program with bigger " "BUFSIZE defined.\n"); exit_code(2, __PRETTY_FUNCTION__, "file to big for buffer"); } fclose(pf); } else if (strlen(optarg) == 1 && STRNCASECMP(optarg, "-", 1) == 0) { if (read_stdin(&buff[0], sizeof(buff), 0) == 0) { exit_code(0, __PRETTY_FUNCTION__, NULL); } } else { fprintf(stderr, "error: unable to handle input file name: %s\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "unsupported input file name"); } options.file_b=1; break; case 'g': options.replace_str=optarg; break; case 'G': options.replace_b=1; break; case 'h': print_help(); break; case 'H': options.hostname=optarg; break; case 'i': options.via_ins=0; break; case 'I': if (options.mode == SM_USRLOC) { options.mode = SM_USRLOC_INVITE; } else { options.mode = SM_INVITE; } break; case 'j': options.headers=optarg; break; case 'J': if (strlen(optarg) < SIPSAK_HASHHEXLEN_MD5) { fprintf(stderr, "error: authhash string is too short\n"); exit_code(2, __PRETTY_FUNCTION__, "authhash string is too short"); } options.authhash=optarg; break; case 'k': options.local_ip=optarg; break; case 'K': sysl=str_to_int(0, optarg); #ifdef HAVE_SYSLOG_H openlog(PACKAGE_NAME, LOG_CONS|LOG_NOWAIT|LOG_PID, LOG_USER); #endif if (sysl < LOG_ALERT || sysl > LOG_DEBUG) { fprintf(stderr, "error: syslog value '%s' must be between ALERT (1) and DEBUG (7)\n", optarg); exit_code(2, __PRETTY_FUNCTION__, "unsupported syslog value for option K"); } break; case 'l': options.lport=str_to_int(0, optarg); break; case 'L': options.fix_crlf=0; break; case 'm': options.maxforw=str_to_int(0, optarg); break; case 'M': if (options.mode == SM_USRLOC) { options.mode = SM_USRLOC_MESSAGE; } else { options.mode = SM_MESSAGE; } break; case 'n': options.numeric = 0; break; case 'N': exit_mode = EM_NAGIOS; break; case 'o': options.sleep_ms = 0; if (strlen(optarg) == 4 && STRNCASECMP(optarg, "rand", 4) == 0) { options.sleep_ms = -2; } else { options.sleep_ms = str_to_int(0, optarg); } break; case 'O': options.con_dis=str_alloc(strlen(optarg) + 1); strncpy(options.con_dis, optarg, strlen(optarg)); break; case 'p': parse_uri(optarg, &scheme, &user, &host, &port); if (host == NULL) { fprintf(stderr, "error: missing in host in outbound proxy\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host in outbound proxy"); } if (is_ip(host)) { options.address = getaddress(host); if (options.transport == 0) options.transport = SIP_UDP_TRANSPORT; } else { if (!port) { options.address = getsrvadr(host, &options.rport, &tsp); if (tsp != 0) options.transport = tsp; } if (!options.address) { options.address = getaddress(host); if (options.address && verbose > 1) printf("using A record: %s\n", host); } if (!options.address){ fprintf(stderr, "error:unable to determine the outbound proxy " "address\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to resolve the outbound proxy"); } } if (port && !options.rport) { options.rport = port; } options.outbound_proxy=1; #ifdef DEBUG printf("address: %lu, rport: %i\n", options.address, options.rport); #endif break; case 'P': options.processes=str_to_int(0, optarg); break; case 'q': if (options.regex) { /* previously allocated -- free */ regfree(options.regex); } else { /* never tried -- allocate */ options.regex=malloc(sizeof(regex_t)); }; if (!options.regex) { fprintf(stderr, "Error: can't allocate RE\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to allocate memory for regualr expression"); }; if (regcomp(options.regex, optarg, REG_EXTENDED|REG_ICASE|REG_NEWLINE )!=0) { fprintf(stderr, "Error: compiling RE: %s\n", optarg ); exit_code(2, __PRETTY_FUNCTION__, "failed to compile regular expression"); }; break; case 'r': port=str_to_int(0, optarg); if (options.rport) { fprintf(stderr, "warning: you are overwritting the destination port with the r argument\n"); } options.rport = port; break; case 'R': options.mode = SM_RANDTRASH; break; case 's': parse_uri(optarg, &scheme, &user, &host, &port); if (scheme == NULL) { fprintf(stderr, "error: missing scheme in SIP URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing scheme in target SIP URI"); } if (strlen(optarg) == 4 && STRNCASECMP(optarg,"sips",4) == 0){ fprintf(stderr, "error: sips is not supported yet\n"); exit_code(2, __PRETTY_FUNCTION__, "unsupported scheme SIPS in target URI"); } else if (strlen(optarg) != 3 || STRNCASECMP(optarg,"sip",3) != 0){ fprintf(stderr, "error: scheme of SIP URI has to be sip\n"); exit_code(2, __PRETTY_FUNCTION__, "unsupported scheme in target URI"); } if (user != NULL) { options.username = user; } if (host != NULL) { options.domainname = host; } else { fprintf(stderr, "error: missing hostname in SIP URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing host name in target URI"); } if (port && !options.rport) { options.rport = port; } if (is_ip(options.domainname) && !options.address) { options.address = getaddress(options.domainname); if (options.transport == 0) options.transport = SIP_UDP_TRANSPORT; } else { if (!options.rport && !options.address) { options.address = getsrvadr(options.domainname, &options.rport, &tsp); if (tsp != 0 && options.transport == 0) options.transport = tsp; } if (!options.address) { options.address = getaddress(options.domainname); if (options.address && verbose > 1) printf("using A record: %s\n", options.domainname); } if (!options.address){ fprintf(stderr, "error:unable to determine the IP address for: %s\n", options.domainname); exit_code(2, __PRETTY_FUNCTION__, "failed to resolve host from target URI"); } } if (port != 0) { backup = str_alloc(strlen(options.domainname)+1+6); snprintf(backup, strlen(options.domainname)+6, "%s:%i", options.domainname, port); options.domainname = backup; } options.uri_b=1; #ifdef DEBUG printf("address: %lu, rport: %i, username: '%s', domain: '%s'\n", options.address, options.rport, options.username, options.domainname); #endif break; case 'S': fprintf(stderr, "warning: symmetric does not work with a-symmetric servers\n"); options.symmetric=1; break; case 't': options.trashchar=str_to_int(0, optarg); break; case 'T': options.mode = SM_TRACE; break; case 'U': if (options.mode == SM_INVITE) { options.mode = SM_USRLOC_INVITE; } else if (options.mode == SM_MESSAGE) { options.mode = SM_USRLOC_MESSAGE; } else { options.mode = SM_USRLOC; } break; case 'u': options.auth_username=str_alloc(strlen(optarg) + 1); strncpy(options.auth_username, optarg, strlen(optarg)); break; case 'v': verbose++; break; case 'V': printf("sipsak %s by Nils Ohlmeier\n Copyright (C) 2002-2004" " FhG Fokus\n Copyright (C) 2004-2005 Nils Ohlmeier\n", SIPSAK_VERSION); printf(" compiled with DEFAULT_TIMEOUT=%i, FQDN_SIZE=%i", DEFAULT_TIMEOUT, FQDN_SIZE); #ifdef RAW_SUPPORT printf(", RAW_SUPPORT"); #endif #ifdef HAVE_GETOPT_LONG printf(", LONG_OPTS"); #endif #ifdef HAVE_FULL_OPENSSL printf(", OPENSSL_MD5"); #else printf(", INTERNAL_MD5"); #endif #ifdef HAVE_OPENSSL_SHA1 printf(", OPENSSL_SHA1"); #endif #ifdef WITH_TLS_TRANSP # ifdef USE_GNUTLS printf(", TLS_SUPPORT(GNUTLS)"); # else # ifdef USE_OPENSSL printf(", TLS_SUPPORT(OPENSSL)"); # endif # endif #endif #ifdef HAVE_CARES_H printf(", SRV_SUPPORT(ARES)"); #else # ifdef HAVE_RULI_H printf(", SRV_SUPPORT(RULI)"); # endif #endif #ifdef HAVE_STRCASESTR printf(", STR_CASE_INSENSITIVE"); #endif #ifdef HAVE_STRNCASECMP printf(", CMP_CASE_INSENSITIVE"); #endif #ifdef DEBUG printf(", DEBUG"); #endif printf("\n"); exit_code(0, __PRETTY_FUNCTION__, NULL); break; case 'w': options.warning_ext=1; break; case 'W': options.nagios_warn = str_to_int(0, optarg); break; case 'x': options.expires_t=str_to_int(0, optarg); break; case 'z': options.rand_rem=str_to_int(0, optarg); if (options.rand_rem < 0 || options.rand_rem > 100) { fprintf(stderr, "error: z option must between 0 and 100\n"); exit_code(2, __PRETTY_FUNCTION__, "value for option z out of range"); } break; case 'Z': options.timer_t1 = str_to_int(0, optarg); if (options.timer_t1 <= 0) { fprintf(stderr, "error: Z option must be above 0\n"); exit_code(2, __PRETTY_FUNCTION__, "value for option Z must be above 0"); } break; default: fprintf(stderr, "error: unknown parameter '%c'\n", c); exit_code(2, __PRETTY_FUNCTION__, "unknown parameter"); break; } } if (options.rport == 0) { options.rport = 5060; } if (options.rport > 65535 || options.rport <= 0) { fprintf(stderr, "error: invalid remote port: %i\n", options.rport); exit_code(2, __PRETTY_FUNCTION__, "remote port out of range"); } if (options.transport == 0) { options.transport = SIP_UDP_TRANSPORT; } /* replace LF with CRLF if we read from a file */ if ((options.file_b) && (options.fix_crlf)) { insert_cr(buff); } if (options.headers) { backup = str_alloc(strlen(options.headers) + 30); // FIXME strcpy(backup, options.headers); options.headers = backup; replace_string(options.headers, "\\n", "\r\n"); backup = options.headers + strlen(options.headers) - 1; if (*backup != '\n') { strcpy(backup + 1, "\r\n"); } if (options.file_b) insert_header(buff, options.headers, 1); } /* lots of conditions to check */ if (options.mode == SM_TRACE) { if (!options.uri_b) { fprintf(stderr, "error: for trace mode a SIPURI is really needed\n"); exit_code(2, __PRETTY_FUNCTION__, "missing URI for trace mode"); } if (options.file_b) { fprintf(stderr, "warning: file will be ignored for tracing."); } if (!options.username) { fprintf(stderr, "error: for trace mode without a file the SIPURI have to " "contain a username\n"); exit_code(2, __PRETTY_FUNCTION__, "missing username in target URI"); } if (!options.via_ins){ fprintf(stderr, "warning: Via-Line is needed for tracing. Ignoring -i\n"); options.via_ins=1; } if (!options.warning_ext) { fprintf(stderr, "warning: IP extract from warning activated to be more " "informational\n"); options.warning_ext=1; } if (options.maxforw==-1) { options.maxforw=255; } } else if (options.mode == SM_USRLOC || options.mode == SM_USRLOC_INVITE || options.mode == SM_USRLOC_MESSAGE || options.mode == SM_INVITE || options.mode == SM_MESSAGE) { if (!options.username || !options.uri_b) { fprintf(stderr, "error: for the USRLOC mode you have to give a SIPURI with " "a username\n at least\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target URI or username in URI"); } if (options.namebeg>0 && options.nameend==-1) { fprintf(stderr, "error: if a starting numbers is given also an ending " "number have to be specified\n"); exit_code(2, __PRETTY_FUNCTION__, "missing end number"); } if (options.mode == SM_USRLOC_INVITE && !options.lport) { fprintf(stderr, "warning: Do NOT use the usrloc invite mode without " "registering sipsak before.\n See man page for " "details.\n"); exit_code(2, __PRETTY_FUNCTION__, "don't use usrloc INVITE mode without registerting before"); } if (options.contact_uri!=NULL) { if (options.mode == SM_USRLOC_INVITE || options.mode == SM_USRLOC_MESSAGE || options.mode == SM_INVITE || options.mode == SM_MESSAGE) { fprintf(stderr, "error: Contact URI is not support for invites or " "messages\n"); exit_code(2, __PRETTY_FUNCTION__, "Contact URI not supported for INVITE or MESSAGE mode"); } if (options.nameend!=-1 || options.namebeg!=-1) { fprintf(stderr, "warning: ignoring starting or ending number if Contact" " is given\n"); options.nameend = 0; options.namebeg = 0; } if (options.rand_rem) { fprintf(stderr, "warning: ignoring -z option when Contact is given\n"); options.rand_rem=0; } } if (options.via_ins) { if (verbose > 1) { fprintf(stderr, "warning: Deactivated Via insertion in usrloc mode.\n Please use option -i to suppress this warning.\n"); } options.via_ins=0; } if (options.nameend==-1) options.nameend=0; if (options.namebeg==-1) options.namebeg=0; } else if (options.mode == SM_FLOOD) { if (!options.uri_b) { fprintf(stderr, "error: we need at least a SIP URI for flood\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target URI"); } if (options.redirects) { fprintf(stderr, "warning: redirects are not expected in flood. " "disabling\n"); options.redirects=0; } } else if (options.mode == SM_RANDTRASH) { if (!options.uri_b) { fprintf(stderr, "error: need at least a SIP URI for random\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target URI"); } if (options.redirects) { fprintf(stderr, "warning: redirects are not expected in random. " "disableing\n"); options.redirects=0; } if (verbose) { fprintf(stderr, "warning: random characters may destroy your terminal " "output\n"); } } else if (options.mes_body) { if (!(options.mode == SM_MESSAGE || options.mode == SM_USRLOC_MESSAGE)) { fprintf(stderr, "warning: to send a message mode (-M) is required. activating\n"); options.mode = SM_MESSAGE; } if (!options.uri_b) { fprintf(stderr, "error: need at least a SIP URI to send a meesage\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target SIP URI"); } if (options.nameend==-1) options.nameend=0; if (options.namebeg==-1) options.namebeg=0; } else { if (!options.uri_b) { fprintf(stderr, "error: a SIP URI is needed at least\n"); exit_code(2, __PRETTY_FUNCTION__, "missing target SIP URI"); } } /* this is not a cryptographic random number generator, but hey this is only a test-tool => should be satisfying*/ srand(time(0) ^ (getpid() + (getpid() << 15))); if (options.processes > 1) { if (signal(SIGCHLD , sigchld_handler) == SIG_ERR ) { fprintf(stderr, "error: Could not install SIGCHLD handler\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to install SIGCHLD handler"); } } for(i = 0; i < options.processes - 1; i++) { if ((pid = fork()) < 0) { fprintf(stderr, "error: Cannot fork\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to fork"); } if (pid == 0){ /* child */ upp = (options.nameend - options.namebeg + 1) / options.processes; options.namebeg = options.namebeg + upp * i; options.nameend = options.namebeg + upp; shoot(&buff[0], sizeof(buff), &options); } else { if (options.lport) { options.lport++; } } /* Delay execution of children so that the * time of the first transmission gets spread over * the retransmission interval evenly */ ts.tv_sec = 0; ts.tv_nsec = (float)DEFAULT_TIMEOUT / (float)options.processes * (float)1000 * (float)1000; nanosleep(&ts, 0); } /* here we go...*/ if (options.processes > 1) { upp = (options.nameend - options.namebeg + 1) / options.processes; options.namebeg = options.namebeg + upp * i; options.nameend = options.namebeg + upp; } shoot(&buff[0], sizeof(buff), &options); /* normaly we won't come back here, but to satisfy the compiler */ return 0; } sipsak-0.9.8.1/src/sipsak.h000066400000000000000000000206361377576723000154430ustar00rootroot00000000000000/* * Copyright (C) 2002-2004 Fhg Fokus * Copyright (C) 2004-2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_H #define SIPSAK_H #if HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_REGEX_H # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifndef INET_ADDRSTRLEN # define INET_ADDRSTRLEN 16 #endif #ifdef HAVE_SIGNAL_H # include #endif #ifdef HAVE_LIMITS_H # include #endif #ifndef INT_MAX # define INT_MAX 2147483648 #endif #ifdef HAVE_STRCASESTR # define __USE_GNU # define STRCASESTR(s1,s2) strcasestr(s1,s2) #else # define STRCASESTR(s1,s2) strstr(s1,s2) #endif #ifdef HAVE_STRNCASECMP # define STRNCASECMP(s1,s2,s3) strncasecmp(s1,s2,s3) #else # define STRNCASECMP(s1,s2,s3) strncmp(s1,s2,s3) #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_GNUTLS # define USE_GNUTLS # ifndef SIPSAK_NO_TLS # define WITH_TLS_TRANSP 1 # endif # include #else # ifdef HAVE_OPENSSL_MD5_H # ifdef HAVE_CRYPTO_WITH_MD5 # define HAVE_FULL_OPENSSL # define HAVE_EXTERNAL_MD5 # define USE_OPENSSL # include # endif # endif #endif #ifdef HAVE_OPENSSL_SHA_H # ifdef HAVE_CRYPTO_WITH_SHA1 # define HAVE_OPENSSL_SHA1 # endif #endif #ifdef SIPSAK_PRINT_DBG # define DEBUG 1 #endif #ifndef REG_NOERROR # define REG_NOERROR 0 #endif #ifdef HAVE_SYS_PARAM_H # ifdef MAXHOSTNAMELEN # define FQDN_SIZE MAXHOSTNAMELEN + 1 # else # define FQDN_SIZE 100 # endif #else # define FQDN_SIZE 100 #endif #ifdef HAVE_CONFIG_H # define SIP_T1 DEFAULT_TIMEOUT #else # define SIP_T1 500 #endif #define SIP_T2 8*SIP_T1 #define SIPSAK_VERSION PACKAGE_VERSION #define UA_VAL_STR "sipsak " SIPSAK_VERSION #define BUFSIZE 4096 #define SIPSAK_MAX_PASSWD_LEN 20 #define REQ_REG 1 #define REQ_REM 2 #define REQ_INV 3 #define REQ_MES 4 #define REQ_OPT 5 #define REQ_FLOOD 6 #define REQ_RAND 7 #define SIP_TLS_TRANSPORT 1 #define SIP_TCP_TRANSPORT 2 #define SIP_UDP_TRANSPORT 3 #define TRANSPORT_TLS_STR "TLS" #define TRANSPORT_TCP_STR "TCP" #define TRANSPORT_UDP_STR "UDP" #define TRANSPORT_STR_LEN 3 #define VIA_SIP_STR "Via: SIP/2.0/" #define VIA_SIP_STR_LEN (sizeof(VIA_SIP_STR) - 1) #define SIP20_STR " SIP/2.0\r\n" #define SIP20_STR_LEN (sizeof(SIP20_STR) - 1) #define SIP200_STR "SIP/2.0 200 OK\r\n" #define SIP200_STR_LEN (sizeof(SIP200_STR) - 1) #define INV_STR "INVITE" #define INV_STR_LEN (sizeof(INV_STR) - 1) #define REG_STR "REGISTER" #define REG_STR_LEN (sizeof(REG_STR) - 1) #define OPT_STR "OPTIONS" #define OPT_STR_LEN (sizeof(OPT_STR) - 1) #define MES_STR "MESSAGE" #define MES_STR_LEN (sizeof(MES_STR) - 1) #define ACK_STR "ACK" #define ACK_STR_LEN (sizeof(ACK_STR) - 1) #define FROM_STR "From: " #define FROM_STR_LEN (sizeof(FROM_STR) - 1) #define FROM_SHORT_STR "\nf: " #define FROM_SHORT_STR_LEN (sizeof(FROM_SHORT_STR) - 1) #define TO_STR "To: " #define TO_STR_LEN (sizeof(TO_STR) - 1) #define TO_SHORT_STR "\nt: " #define TO_SHORT_STR_LEN (sizeof(TO_SHORT_STR) - 1) #define VIA_STR "Via: " #define VIA_STR_LEN (sizeof(VIA_STR) - 1) #define VIA_SHORT_STR "\nv: " #define VIA_SHORT_STR_LEN (sizeof(VIA_SHORT_STR) - 1) #define CALL_STR "Call-ID: " #define CALL_STR_LEN (sizeof(CALL_STR) - 1) #define CALL_SHORT_STR "\ni: " #define CALL_SHORT_STR_LEN (sizeof(CALL_SHORT_STR) - 1) #define MAX_FRW_STR "Max-Forwards: " #define MAX_FRW_STR_LEN (sizeof(MAX_FRW_STR) - 1) #define CSEQ_STR "CSeq: " #define CSEQ_STR_LEN (sizeof(CSEQ_STR) - 1) #define CONT_STR "Contact: " #define CONT_STR_LEN (sizeof(CONT_STR) - 1) #define CONT_SHORT_STR "\nm: " #define CONT_SHORT_STR_LEN (sizeof(CONT_SHORT_STR) - 1) #define CON_TYP_STR "Content-Type: " #define CON_TYP_STR_LEN (sizeof(CON_TYP_STR) - 1) #define CON_TYP_SHORT_STR "\nc: " #define CON_TYP_SHORT_STR_LEN (sizeof(CON_TYP_SHORT_STR) - 1) #define CON_DIS_STR "Content-Disposition: " #define CON_DIS_STR_LEN (sizeof(CON_DIS_STR) - 1) #define TXT_PLA_STR "text/plain" #define TXT_PLA_STR_LEN (sizeof(TXT_PLA_STR) - 1) #define ACP_STR "Accept: " #define ACP_STR_LEN (sizeof(ACP_STR) - 1) #define CON_LEN_STR "Content-Length: " #define CON_LEN_STR_LEN (sizeof(CON_LEN_STR) - 1) #define CON_LEN_SHORT_STR "\nl: " #define CON_LEN_SHORT_STR_LEN (sizeof(CON_LEN_SHORT_STR) - 1) #define RR_STR "Record-Route: " #define RR_STR_LEN (sizeof(RR_STR) - 1) #define ROUTE_STR "Route: " #define ROUTE_STR_LEN (sizeof(ROUTE_STR) - 1) #define SIPSAK_MES_STR "test message from SIPsak for user " #define SIPSAK_MES_STR_LEN (sizeof(SIPSAK_MES_STR) - 1) #define EXP_STR "Expires: " #define EXP_STR_LEN (sizeof(EXP_STR) - 1) #define CON_EXP_STR "expires=" #define CON_EXP_STR_LEN (sizeof(CON_EXP_STR) - 1) #define WWWAUTH_STR "WWW-Authenticate: " #define WWWAUTH_STR_LEN (sizeof(WWWAUTH_STR) - 1) #define PROXYAUTH_STR "Proxy-Authenticate: " #define PROXYAUTH_STR_LEN (sizeof(PROXYAUTH_STR) - 1) #define AUTH_STR "Authorization: Digest " #define AUTH_STR_LEN (sizeof(AUTH_STR) - 1) #define PROXYAUZ_STR "Proxy-Authorization: Digest " #define PROXYAUZ_STR_LEN (sizeof(PROXYAUZ_STR) - 1) #define ALGO_STR "algorithm=" #define ALGO_STR_LEN (sizeof(ALGO_STR) - 1) #define MD5_STR "MD5, " #define MD5_STR_LEN (sizeof(MD5_STR) - 1) #define SHA1_STR "SHA1, " #define SHA1_STR_LEN (sizeof(SHA1_STR) - 1) #define SHA256_STR "SHA-256, " #define SHA256_STR_LEN (sizeof(SHA256_STR) - 1) #define REALM_STR "realm=" #define REALM_STR_LEN (sizeof(REALM_STR) - 1) #define OPAQUE_STR "opaque=" #define OPAQUE_STR_LEN (sizeof(OPAQUEE_STR) - 1) #define NONCE_STR "nonce=" #define NONCE_STR_LEN (sizeof(NONCE_STR) - 1) #define RESPONSE_STR "response=" #define RESPONSE_STR_LEN (sizeof(RESPONSE_STR) - 1) #define QOP_STR "qop=" #define QOP_STR_LEN (sizeof(QOP_STR) - 1) #define QOPAUTH_STR "auth" #define QOPAUTH_STR_LEN (sizeof(QOPAUTH_STR) - 1) #define NC_STR "nc=" #define NC_STR_LEN (sizeof(NC_STR) - 1) #define EMPTY_STR "" #define EMPTY_STR_LEN (sizeof(EMPTY_STR) - 1) #define UA_STR "User-Agent: " #define UA_STR_LEN (sizeof(UA_STR) - 1) #define SUB_STR "Subject: " #define SUB_STR_LEN (sizeof(SUB_STR) - 1) #define SIP100_STR "SIP/2.0 100" #define SIP100_STR_LEN (sizeof(SIP100_STR) - 1) #define TRANSPORT_PARAMETER_STR ";transport=" #define TRANSPORT_PARAMETER_STR_LEN (sizeof(TRANSPORT_PARAMETER_STR) - 1) #define USRLOC_EXP_DEF 15 #define FLOOD_METH "OPTIONS" #define SIPSAK_HASHLEN_MD5 16 #define SIPSAK_HASHHEXLEN_MD5 2 * SIPSAK_HASHLEN_MD5 #ifdef HAVE_OPENSSL_SHA1 # define SIPSAK_HASHLEN_SHA1 20 # define SIPSAK_HASHHEXLEN_SHA1 2 * SIPSAK_HASHLEN_SHA1 # define SIPSAK_HASHLEN_SHA256 32 # define SIPSAK_HASHHEXLEN_SHA256 2 * SIPSAK_HASHLEN_SHA256 # define SIPSAK_HASHLEN SIPSAK_HASHLEN_SHA256 #else # define SIPSAK_HASHLEN SIPSAK_HASHLEN_MD5 #endif #define SIPSAK_HASHHEXLEN 2 * SIPSAK_HASHLEN extern int verbose; enum sipsak_modes { SM_UNDEFINED, SM_USRLOC, SM_USRLOC_INVITE, SM_USRLOC_MESSAGE, SM_INVITE, SM_MESSAGE, SM_FLOOD, SM_TRACE, SM_RANDTRASH }; struct sipsak_options { int timing; int namebeg; int nameend; int empty_contact; int redirects; int timer_final; int file_b; int replace_b; int via_ins; int lport; int rport; int fix_crlf; int maxforw; int numeric; int sleep_ms; int outbound_proxy; int processes; int randtrash; int trashchar; int uri_b; int symmetric; int warning_ext; int nagios_warn; int expires_t; int rand_rem; int timer_t1; #ifdef WITH_TLS_TRANSP int ignore_ca_fail; #endif enum sipsak_modes mode; char *password; char *mes_body; char *from_uri; char *contact_uri; char *replace_str; char *hostname; char *headers; char *authhash; char *local_ip; char *con_dis; char *username; char *domainname; char *auth_username; #ifdef WITH_TLS_TRANSP char *cert_file; char *ca_file; #endif unsigned int transport; unsigned long address; regex_t *regex; }; #endif sipsak-0.9.8.1/src/transport.c000066400000000000000000001140641377576723000161770ustar00rootroot00000000000000/* * Copyright (C) 2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sipsak.h" #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif /* TIME_WITH_SYS_TIME */ #ifdef HAVE_UNISTD_H # ifdef HAVE_SYS_TYPES_H # include # endif # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #ifdef HAVE_SYS_POLL_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #include "transport.h" #include "shoot.h" #ifdef RAW_SUPPORT # ifdef HAVE_NETINET_IN_SYSTM_H # include # endif # ifdef HAVE_NETINET_IP_H # include # endif # ifdef HAVE_NETINET_IP_ICMP_H # include # endif # ifdef HAVE_NETINET_UDP_H # define __FAVOR_BSD # include # endif #endif /* RAW_SUPPORT */ #ifdef WITH_TLS_TRANSP # ifdef USE_GNUTLS # include # include # include # include # include # include # include # include # include # include # else # ifdef USE_OPENSSL # define _BSD_SOURCE 1 # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # endif # endif #endif /* WITH_TLS_TRANSP */ #include "exit_code.h" #include "helper.h" #include "header_f.h" char *transport_str; char target_dot[INET_ADDRSTRLEN], source_dot[INET_ADDRSTRLEN]; #ifdef RAW_SUPPORT int rawsock; #endif #ifdef USE_GNUTLS gnutls_session_t tls_session; //gnutls_anon_client_credentials_t anoncred; gnutls_certificate_credentials_t xcred; #else # ifdef USE_OPENSSL SSL_CTX* ctx; SSL* ssl; # endif #endif #ifdef WITH_TLS_TRANSP # ifdef USE_GNUTLS void check_alert(gnutls_session_t session, int ret) { int last_alert; if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) { last_alert = gnutls_alert_get(session); printf("Received TLS alert: '%d': %s\n", last_alert, gnutls_alert_get_name(last_alert)); } } /* all the available CRLs */ gnutls_x509_crl_t *global_crl_list; int global_crl_list_size; /* all the available trusted CAs */ gnutls_x509_crt_t *global_ca_list; int global_ca_list_size; /* verifies a certificate against an other certificate which is supposed to * be it's issuer. Also checks the crl_list of the certificate is revoked. */ static void verify_cert2(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, gnutls_x509_crl_t *crl_list, int crl_list_size) { unsigned int output; time_t now = time(0); size_t name_size; char name[64]; /* print information about the certificates to be checked */ name_size = sizeof(name); gnutls_x509_crt_get_dn(crt, name, &name_size); printf("Certificate: %s\n", name); name_size = sizeof(name); gnutls_x509_crt_get_issuer_dn(crt, name, &name_size); printf("Issued by: %s\n", name); /* Get the DN of the issuer cert. */ name_size = sizeof(name); gnutls_x509_crt_get_dn(issuer, name, &name_size); printf("Checking against: %s\n", name); /* Do the actual verification */ gnutls_x509_crt_verify(crt, &issuer, 1, 0, &output); if (output & GNUTLS_CERT_INVALID) { printf("Certificate not trusted!!!"); if (output & GNUTLS_CERT_SIGNER_NOT_FOUND) { printf(": no issuer was found\n"); } if (output & GNUTLS_CERT_SIGNER_NOT_CA) { printf(": issuer is not a CA\n"); } } else { printf("Certificate trusted'n"); } /* Now check the expiration dates */ if (gnutls_x509_crt_get_activation_time(crt) > now) { printf("Certificate is not yet valid!\n"); } if (gnutls_x509_crt_get_expiration_time(crt) < now) { printf("Certificate expired!\n"); } /* Check if the certificate is revoked */ if (gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size) == 1) { printf("Certificate is revoked!\n"); } } /* Verifies a certificate against our trusted CA list. Also checks the crl_list * if the certificate is revoked */ static void verify_last_cert(gnutls_x509_crt_t crt, gnutls_x509_crt_t *ca_list, int ca_list_size, gnutls_x509_crl_t *crl_list, int crl_list_size) { unsigned int output; time_t now = time(0); size_t name_size; char name[64]; /* Print information about the certificates to be checked */ name_size = sizeof(name); gnutls_x509_crt_get_dn(crt, name, &name_size); printf("Certificate: %s\n", name); name_size = sizeof(name); gnutls_x509_crt_get_issuer_dn(crt, name, &name_size); printf("Issued by: %s\n", name); /* Do the actual verification */ gnutls_x509_crt_verify(crt, ca_list, ca_list_size, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &output); if (output & GNUTLS_CERT_INVALID) { printf("Certificate not trusted!\n"); if (output & GNUTLS_CERT_SIGNER_NOT_CA) { printf(": Issuer is not a CA\n"); } } else { printf("Certificate trusted\n"); } /* Now check the expiration dates */ if (gnutls_x509_crt_get_activation_time(crt) > now) { printf("Certificate is not yet valid!\n"); } if (gnutls_x509_crt_get_expiration_time(crt) < now) { printf("Certificate expired!\n"); } /* Check of the vertificate is revoked */ if (gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size) == 1) { printf("Certificate is revoked!\n"); } } /* this function will try yo verify the peer's certificate chain, ans * also check if the hostname matches, and the activation and expiration dates. */ void verify_certificate_chain(gnutls_session_t session, const char *hostname, const gnutls_datum_t *cert_chain, int cert_chain_length) { int i; gnutls_x509_crt_t *cert; cert = malloc(sizeof(*cert) * cert_chain_length); if (!cert) { printf("gnutls: failed to allocate memory for cert chain verification'n"); return; } /* import all the certificates in the chain to native certificate format */ for (i = 0; i < cert_chain_length; i++) { gnutls_x509_crt_init(&cert[i]); gnutls_x509_crt_import(cert[i], &cert_chain[i], GNUTLS_X509_FMT_DER); } /* if the last certificate in the chain is seld signed ignore it. * that is because we want to check against our trusted certificate list */ if (gnutls_x509_crt_check_issuer(cert[cert_chain_length - 1], cert[cert_chain_length -1]) > 0 && cert_chain_length > 0) { cert_chain_length--; } /* now verify the certificates against other issuers in the chain */ for (i = 1; i < cert_chain_length; i++) { verify_cert2(cert[i - 1], cert[i], global_crl_list, global_crl_list_size); } /* here we must verify the last certificate in the chain against our * trusted CA list */ verify_last_cert(cert[cert_chain_length - 1], global_ca_list, global_ca_list_size, global_crl_list, global_crl_list_size); /* check if the name in the first certificate matches our destination */ if (!gnutls_x509_crt_check_hostname(cert[0], hostname)) { printf("The certificate's owner does not match hostname '%s'\n", hostname); } for (i = 0; i < cert_chain_length; i++) { gnutls_x509_crt_deinit(cert[i]); } return; } int verify_certificate_simple(gnutls_session_t session, const char *hostname, int ignore_ca_fail) { unsigned int status, cert_list_size; const gnutls_datum_t *cert_list; int ret; gnutls_x509_crt_t cert; // this verification function usese the trusted CAs in the credentials // structure. so you must have installed on or more CA certificates. ret = gnutls_certificate_verify_peers2(session, &status); if (ret < 0) { printf("gnutls verify peer failed.\n"); return -1; } ret = 0; if (status & GNUTLS_CERT_INVALID) { ret |= -2; printf("The certificate is not trustworthy\n"); if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { printf("The certificate hasn't got a known issuer.\n"); ret |= -4; } if (status & GNUTLS_CERT_SIGNER_NOT_CA) { printf("The certificate issuer is not a CA\n"); ret |= -8; } } if (status & GNUTLS_CERT_REVOKED) { printf("The certificate has been revoked.\n"); ret = -16; } if (ret != 0 && ignore_ca_fail == 0) { return ret; } // from here on it works only with X509 certs if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509){ printf("The server certificate is not X509.\n"); return -32;; } if (gnutls_x509_crt_init(&cert) < 0) { printf("gnutls crt init failed.\n"); return -64; } cert_list = gnutls_certificate_get_peers(session, &cert_list_size); if (cert_list == NULL) { printf("gnutls did not find a server certificate.\n"); return -128; } // this not a real world check as only the first cert is checked! if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) { printf("gnutls failed to parse server certificate.\n"); return -256; } // beware here we do not check for errors if (gnutls_x509_crt_get_expiration_time(cert) < time(0)) { printf("The server certificate is expired.\n"); return -512; } if (gnutls_x509_crt_get_activation_time(cert) > time(0)) { printf("The server certificate is not yet valid.\n"); return -1024; } if (!gnutls_x509_crt_check_hostname(cert, hostname)) { printf("The server certificate's owner does not match hostname '%s'\n", hostname); return -2048; } gnutls_x509_crt_deinit(cert); return ret; } static const char *bin2hex(const void *bin, size_t bin_size) { static char printable[110]; const unsigned char *_bin = bin; char *print; size_t i; if (bin_size > 50) { bin_size = 50; } print = printable; for (i=0; i < bin_size; i++) { sprintf(print, "%.2x ", _bin[i]); print += 2; } return printable; } void print_x509_certificate_info(gnutls_session_t session) { char serial[40]; char dn[128]; size_t size; unsigned int algo, bits; time_t expiration_time, activation_time; const gnutls_datum_t *cert_list; unsigned int cert_list_size = 0; gnutls_x509_crt_t cert; // check if we got a X.509 cert if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) { printf("TLS session did not receive a X.509 certificate\n"); return; } cert_list = gnutls_certificate_get_peers(session, &cert_list_size); printf("Peer provided %u certificate(s)\n", cert_list_size); if (cert_list_size > 0) { // print only information about the first cert gnutls_x509_crt_init(&cert); gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); printf("Certificate info:\n"); activation_time = gnutls_x509_crt_get_activation_time(cert); printf("\tCertificate is valid since: %s", ctime(&activation_time)); expiration_time = gnutls_x509_crt_get_expiration_time(cert); printf("\tCertificate expires: %s", ctime(&expiration_time)); // print the serial number of the certificate size = sizeof(serial); gnutls_x509_crt_get_serial(cert, serial, &size); printf("\tCertificate serial number: %s\n", bin2hex(serial, size)); // extract public key algorithm algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); printf("\tCertificate public key algorithm: %s\n", gnutls_pk_algorithm_get_name(algo)); // print version of x509 cert printf("\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert)); // print name of the certificate size = sizeof(dn); gnutls_x509_crt_get_dn(cert, dn, &size); printf("\tDN: %s\n", dn); // print subject alt name of the certificate size = sizeof(dn); if (gnutls_x509_crt_get_subject_alt_name(cert, 0, dn, &size, NULL) == 0) { printf("\tSubject Alt Name: %s\n", dn); } // print the algorithm which was used for signing the cert algo = gnutls_x509_crt_get_signature_algorithm(cert); printf("\tCA's signature algorithm: %s\n", gnutls_pk_algorithm_get_name(algo)); // print the name of the CA size = sizeof(dn); if (gnutls_x509_crt_get_issuer_dn(cert, dn, &size) == 0) { printf("\tCA's DN: %s\n", dn); } // print the CA status flags if present if (gnutls_x509_crt_get_ca_status(cert, &algo) > 0 && algo != 0) { printf("\tCA status flag is set\n"); } // print the fingerprint of the cert size = sizeof(dn); // FIXME if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, dn, &size) == 0) { printf("\tFingerprint of the certificate: %s\n", dn); } gnutls_x509_crt_deinit(cert); } } void gnutls_session_info(gnutls_session_t session) { const char *tmp; gnutls_credentials_type_t cred; gnutls_kx_algorithm_t kx; // print the key exchange algorithm name kx = gnutls_kx_get(session); tmp = gnutls_kx_get_name(kx); printf("Key Exchange: %s\n", tmp); // check the authentication type cred = gnutls_auth_get_type(session); switch(cred) { #ifdef HAVE_GNUTLS_SRP case GNUTLS_CRD_SRP: printf("SRP session with username %s\n", gnutls_srp_server_get_username(session)); break; #endif // HAVE_GNUTLS_SRP case GNUTLS_CRD_ANON: printf("Anonymous DH using prime of %d bits\n", gnutls_dh_get_prime_bits(session)); break; case GNUTLS_CRD_CERTIFICATE: // check if we have been using ephemeral DH if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) { printf("Ephemeral DH using prime of %d bits\n", gnutls_dh_get_prime_bits(session)); } // print certificate informations if available print_x509_certificate_info(session); break; default: printf("UNKNOWN GNUTLS authentication type!!!\n"); } // print protocols name tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(session)); printf("Protocol: %s\n", tmp); // print certificate type tmp = gnutls_certificate_type_get_name(gnutls_certificate_type_get(session)); printf("Certificate Type: %s\n", tmp); // print the compression algorithm tmp = gnutls_compression_get_name(gnutls_compression_get(session)); printf("Compression: %s\n", tmp); // print name of the cipher tmp = gnutls_cipher_get_name(gnutls_cipher_get(session)); printf("Cipher: %s\n", tmp); // print the MAC algorithm tmp = gnutls_mac_get_name(gnutls_mac_get(session)); printf("MAC: %s\n", tmp); } # else # ifdef USE_OPENSSL void set_tls_options() { #if OPENSSL_VERSION_NUMBER >= 0x0009070000 /* 0.9.7 */ SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_CIPHER_SERVER_PREFERENCE); #else SSL_CTX_set_options(ctx, SSL_OP_ALL); #endif } void create_tls_ctx() { SSL_METHOD *method = NULL; method = TLSv1_method(); ctx = SSL_CTX_new(method); if (ctx == NULL) { ERR_print_errors_fp(stderr); perror("create_tls_ctx: failed to create TLS ctx"); exit_code(2, __PRETTY_FUNCTION__, "failed to create TLS ctx"); } /*if (!SSL_CTX_use_certificate_chain_file(ctx, cert_file)) { perror("create_tls_ctx: failed to load certificate file"); exit_code(2); } if (SSL_CTX_load_verify_locations(ctx, ca_file, 0) != 1) { perror("create_tls_ctx: failed to load CA cert"); exit_code(2); } SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(ca_file)); if (SSL_CTX_get_client_CA_list(ctx) == 0) { perror("create_tls_ctx: failed to set client CA list"); exit_code(2); }*/ SSL_CTX_set_cipher_list(ctx, 0); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); SSL_CTX_set_verify_depth(ctx, 5); set_tls_options(); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_session_id_context(ctx, 0, 0); } void tls_dump_cert_info(char* s, X509* cert) { char *subj, *issuer; subj = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); printf("%s subject: '%s'\n", s ? s: "", subj); printf("%s issuer: '%s'\n", s ? s : "", issuer); OPENSSL_free(subj); OPENSSL_free(issuer); } # endif /* USE_OPENSSL */ # endif /* USE_GNUTLS */ #endif /* WITH_TLS_TRANSP */ void init_network(struct sipsak_con_data *cd, char *local_ip #ifdef WITH_TLS_TRANSP , char *ca_file #endif ) { socklen_t slen; transport_str = NULL; memset(&target_dot, 0, INET_ADDRSTRLEN); memset(&source_dot, 0, INET_ADDRSTRLEN); #ifdef RAW_SUPPORT rawsock = -1; #endif switch (cd->transport) { #ifdef WITH_TLS_TRANSP case SIP_TLS_TRANSPORT: transport_str = TRANSPORT_TLS_STR; break; #endif /* WITH_TLS_TRANSP */ case SIP_TCP_TRANSPORT: transport_str = TRANSPORT_TCP_STR; break; case SIP_UDP_TRANSPORT: transport_str = TRANSPORT_UDP_STR; break; default: fprintf(stderr, "unknown transport: %u\n", cd->transport); exit_code(2, __PRETTY_FUNCTION__, "unknown transport"); } #ifdef WITH_TLS_TRANSP if (cd->transport == SIP_TLS_TRANSPORT) { # ifdef USE_GNUTLS tls_session = NULL; xcred = NULL; gnutls_global_init(); //gnutls_anon_allocate_client_credentials(&anoncred); gnutls_certificate_allocate_credentials(&xcred); if (ca_file != NULL) { // set the trusted CA file gnutls_certificate_set_x509_trust_file(xcred, ca_file, GNUTLS_X509_FMT_PEM); } # else # ifdef USE_OPENSSL ctx = NULL; ssl = NULL; SSL_library_init(); SSL_load_error_strings(); # endif # endif } #endif /* WITH_TLS_TRANSP */ memset(&(cd->adr), 0, sizeof(struct sockaddr_in)); cd->adr.sin_family = AF_INET; if(local_ip) { cd->adr.sin_addr.s_addr = inet_addr(local_ip); } else { cd->adr.sin_addr.s_addr = htonl(INADDR_ANY); } cd->adr.sin_port = htons((short)cd->lport); if (cd->transport == SIP_UDP_TRANSPORT) { /* create the un-connected socket */ if (!cd->symmetric) { cd->usock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (cd->usock==-1) { perror("unconnected UDP socket creation failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to create unconnected UDP socket"); } if (bind(cd->usock, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr_in) )==-1) { perror("unconnected UDP socket binding failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to bind unconnected UDP socket"); } } #ifdef RAW_SUPPORT /* try to create the raw socket */ rawsock = (int)socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); if (rawsock==-1) { if (verbose>1) fprintf(stderr, "warning: need raw socket (root privileges) to receive all ICMP errors\n"); #endif /* create the connected socket as a primitve alternative to the raw socket*/ cd->csock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (cd->csock==-1) { perror("connected UDP socket creation failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to create connected UDP socket"); } if (!cd->symmetric) cd->adr.sin_port = htons((short)0); if (bind(cd->csock, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr_in) )==-1) { perror("connected UDP socket binding failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to bind connected UDP socket"); } #ifdef RAW_SUPPORT } else if (cd->symmetric) { cd->csock = (int)socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (cd->csock==-1) { perror("connected UDP socket creation failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to create connected UDP socket"); } if (bind(cd->csock, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr_in) )==-1) { perror("connected UDP socket binding failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to bind connected UDP socket"); } } #endif } else { cd->csock = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (cd->csock==-1) { perror("TCP socket creation failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to create TCP socket"); } if (bind(cd->csock, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr_in) )==-1) { perror("TCP socket binding failed"); exit_code(2, __PRETTY_FUNCTION__, "failed to bind TCP socket"); } #ifdef WITH_TLS_TRANSP if (cd->transport == SIP_TLS_TRANSPORT) { #ifdef USE_GNUTLS // initialixe the TLS session gnutls_init(&tls_session, GNUTLS_CLIENT); // use default priorities gnutls_set_default_priority(tls_session); // put the X509 credentials to the session gnutls_credentials_set(tls_session, GNUTLS_CRD_CERTIFICATE, xcred); // add the FD to the session # ifdef HAVE_GNUTLS_319 gnutls_transport_set_int(tls_session, cd->csock); # else gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr_t)(intptr_t)cd->csock); # endif #else /* USE_GNUTLS */ # ifdef USE_OPENSSL create_tls_ctx(); ssl = SSL_new(ctx); if (ssl == NULL) { perror("TLS failed to create SSL object"); exit_code(2, __PRETTY_FUNCTION__, "failed to create SSL object"); } if (SSL_set_fd(ssl, cd->csock) != 1) { perror("TLS failed to add socket to SSL object"); exit_code(2, __PRETTY_FUNCTION__, "failed to add socket to SSL object"); } # endif /* USE_OPENSSL */ #endif /* USE_GNUTLS */ dbg("initialized tls socket %i\n", cd->csock); } #endif /* WITH_TLS_TRANSP */ } /* for the via line we need our listening port number */ if (cd->lport==0){ memset(&(cd->adr), 0, sizeof(struct sockaddr_in)); slen=sizeof(struct sockaddr_in); if (cd->symmetric || cd->transport != SIP_UDP_TRANSPORT) getsockname(cd->csock, (struct sockaddr *) &(cd->adr), &slen); else getsockname(cd->usock, (struct sockaddr *) &(cd->adr), &slen); cd->lport=ntohs(cd->adr.sin_port); } } void shutdown_network() { # ifdef USE_GNUTLS if (tls_session) { gnutls_deinit(tls_session); } if (xcred) { gnutls_certificate_free_credentials(xcred); } gnutls_global_deinit(); # else /* USE_GNUTLS */ # ifdef USE_OPENSSL # endif /* USE_OPENSSL */ # endif /* USE_GNUTLS */ } void send_message(char* mes, struct sipsak_con_data *cd, struct sipsak_counter *sc, struct sipsak_sr_time *srt) { struct timezone tz; int ret = -1; if (cd->dontsend == 0) { if (verbose > 2) { printf("\nrequest:\n%s", mes); } /* lets fire the request to the server and store when we did */ if (cd->csock == -1) { dbg("\nusing un-connected socket for sending\n"); ret = sendto(cd->usock, mes, strlen(mes), 0, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr)); } else { dbg("\nusing connected socket for sending\n"); #ifdef WITH_TLS_TRANSP if (cd->transport == SIP_TLS_TRANSPORT) { # ifdef USE_GNUTLS ret = gnutls_record_send(tls_session, mes, strlen(mes)); # else /* USE_GNUTLS */ # ifdef USE_OPENSSL # endif /* USE_OPENSSL */ # endif /* USE_GNUTLS */ } else { #endif /* TLS_TRANSP */ ret = send(cd->csock, mes, strlen(mes), 0); #ifdef WITH_TLS_TRANSP } #endif /* TLS_TRANSP */ } (void)gettimeofday(&(srt->sendtime), &tz); if (ret==-1) { if (verbose) printf("\n"); perror("send failure"); exit_code(2, __PRETTY_FUNCTION__, "send failure"); } #ifdef HAVE_INET_NTOP if (verbose > 2) { printf("\nsend to: %s:%s:%i\n", transport_str, target_dot, cd->rport); } #endif sc->send_counter++; } else { cd->dontsend = 0; } } void check_socket_error(int socket, char *buffer, int size, enum sipsak_modes mode, char *request) { struct pollfd sockerr; int ret = 0; /* lets see if we at least received an icmp error */ sockerr.fd=socket; sockerr.events=POLLERR; ret = poll(&sockerr, 1, 10); if (ret==1) { if (sockerr.revents & POLLERR) { recvfrom(socket, buffer, size, 0, NULL, 0); if (verbose) printf("\n"); perror("send failure"); if (mode == SM_RANDTRASH) { printf ("last message before send failure:\n%s\n", request); log_message(request); } exit_code(3, __PRETTY_FUNCTION__, "send failure"); } } } int check_for_message(char *recv, int size, struct sipsak_con_data *cd, struct sipsak_sr_time *srt, struct sipsak_counter *count, struct sipsak_delay *sd, enum sipsak_modes mode, int cseq_counter, char *request, char *response, int inv_trans) { fd_set fd; struct timezone tz; struct timeval tv; double senddiff; int ret = 0; if (cd->dontrecv == 0) { /* set the timeout and wait for a response */ tv.tv_sec = sd->retryAfter/1000; tv.tv_usec = (sd->retryAfter % 1000) * 1000; FD_ZERO(&fd); if (cd->usock != -1) FD_SET(cd->usock, &fd); if (cd->csock != -1) FD_SET(cd->csock, &fd); #ifdef RAW_SUPPORT if (rawsock != -1) FD_SET(rawsock, &fd); #endif ret = select(FD_SETSIZE, &fd, NULL, NULL, &tv); (void)gettimeofday(&(srt->recvtime), &tz); } else { cd->dontrecv = 0; } /* store the time of our first send */ if (count->send_counter==1) { memcpy(&(srt->firstsendt), &(srt->sendtime), sizeof(struct timeval)); } if (sd->retryAfter == srt->timer_t1) { memcpy(&(srt->starttime), &(srt->sendtime), sizeof(struct timeval)); } if (ret == 0) { /* lets see if we at least received an icmp error */ if (cd->csock == -1) check_socket_error(cd->usock, recv, size, mode, request); else check_socket_error(cd->csock, recv, size, mode, request); /* printout that we did not received anything */ if (verbose > 0) { if (mode == SM_TRACE) { printf("%i: timeout after %i ms\n", count->namebeg, sd->retryAfter); } else if (mode == SM_USRLOC || mode == SM_INVITE || mode == SM_MESSAGE) { printf("timeout after %i ms\n", sd->retryAfter); } else { printf("** timeout after %i ms**\n", sd->retryAfter); } } if (mode == SM_RANDTRASH) { printf("did not get a response on this request:\n%s\n", request); if (cseq_counter < count->nameend) { if (count->randretrys == 2) { printf("sent the following message three " "times without getting a response:\n%s\n" "give up further retransmissions...\n", request); log_message(request); exit_code(3, __PRETTY_FUNCTION__, "too many retransmissions, giving up..."); } else { printf("resending it without additional " "random changes...\n\n"); (count->randretrys)++; } } } senddiff = deltaT(&(srt->starttime), &(srt->recvtime)); if (senddiff > (double)srt->timer_final) { if (srt->timing == 0) { if (verbose>0) printf("*** giving up, no final response after %.3f ms\n", senddiff); log_message(request); exit_code(3, __PRETTY_FUNCTION__, "timeout (no final response)"); } else { srt->timing--; count->run++; sd->all_delay += senddiff; sd->big_delay = senddiff; new_transaction(request, response); sd->retryAfter = srt->timer_t1; if (srt->timing == 0) { printf("%.3f/%.3f/%.3f ms\n", sd->small_delay, sd->all_delay / count->run, sd->big_delay); log_message(request); exit_code(3, __PRETTY_FUNCTION__, "timeout (no final response)"); } } } else { /* set retry time according to RFC3261 */ if ((inv_trans) || (sd->retryAfter *2 < srt->timer_t2)) { sd->retryAfter = sd->retryAfter * 2; } else { sd->retryAfter = srt->timer_t2; } } (count->retrans_s_c)++; if (srt->delaytime.tv_sec == 0) memcpy(&(srt->delaytime), &(srt->sendtime), sizeof(struct timeval)); /* if we did not exit until here lets try another send */ return -1; } else if ( ret == -1 ) { perror("select error"); exit_code(2, __PRETTY_FUNCTION__, "internal select error"); } else if (((cd->usock != -1) && FD_ISSET(cd->usock, &fd)) || ((cd->csock != -1) && FD_ISSET(cd->csock, &fd))) { if ((cd->usock != -1) && FD_ISSET(cd->usock, &fd)) ret = cd->usock; else if ((cd->csock != -1) && FD_ISSET(cd->csock, &fd)) ret = cd->csock; else { printf("unable to determine the socket which received something\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to determine receiving socket"); } /* no timeout, no error ... something has happened :-) */ if ((mode == SM_FLOOD || mode == SM_UNDEFINED) && (verbose > 1)) printf ("\nmessage received"); } #ifdef RAW_SUPPORT else if ((rawsock != -1) && FD_ISSET(rawsock, &fd)) { if (verbose > 1) printf("\nreceived ICMP message"); ret = rawsock; } #endif else { printf("\nselect returned successfully, nothing received\n"); return -1; } return ret; } int complete_mes(char *mes, int size) { int cl = 0, headers = 0, len = 0; char *tmp = NULL; cl = get_cl(mes); dbg("CL: %i\n", cl); if (cl < 0){ if (verbose > 0) printf("missing CL header; waiting for more bytes...\n"); return 0; } tmp = get_body(mes); dbg("body: '%s'\n", tmp); headers = tmp - mes; dbg("length: %i, headers: %i\n", size, headers); len = headers + cl; if (len == size) { if (verbose > 0) printf("message is complete\n"); return 1; } else if (len > size) { if (verbose > 0) printf("waiting for more bytes...\n"); return 0; } else { /* we received more then the sender claims to sent * for now we treat this as a complete message * FIXME: should we store the extra bytes in a buffer and * truncate the message at the calculated length !? */ if (verbose > 0) printf("received too much bytes...\n"); return 1; } } int recv_message(char *buf, int size, int inv_trans, struct sipsak_delay *sd, struct sipsak_sr_time *srt, struct sipsak_counter *count, struct sipsak_con_data *cd, struct sipsak_regexp *reg, enum sipsak_modes mode, int cseq_counter, char *request, char *response) { int ret = 0; int sock = 0; double tmp_delay; #ifdef HAVE_INET_NTOP struct sockaddr_in peer_adr; socklen_t psize = sizeof(peer_adr); #endif #ifdef RAW_SUPPORT struct sockaddr_in faddr; struct ip *r_ip_hdr, *s_ip_hdr; struct icmp *icmp_hdr; struct udphdr *udp_hdr; size_t r_ip_len, s_ip_len, icmp_len; int srcport, dstport; unsigned int flen; #endif if (cd->buf_tmp) { buf = cd->buf_tmp; size = size - cd->buf_tmp_size; } sock = check_for_message(buf, size, cd, srt, count, sd, mode, cseq_counter, request, response, inv_trans); if (sock <= 1) { return -1; } #ifdef RAW_SUPPORT if (sock != rawsock) { #else else { #endif check_socket_error(sock, buf, size, mode, request); #ifdef WITH_TLS_TRANSP if (cd->transport == SIP_TLS_TRANSPORT) { # ifdef USE_GNUTLS ret = gnutls_record_recv(tls_session, buf, size); # else /* USE_GNUTLS */ # ifdef USE_OPENSSL # endif /* USE_OPENSSL */ # endif /* USE_GNUTLS */ } else { #endif /* TLS_TRANSP */ ret = recvfrom(sock, buf, size, 0, NULL, 0); #ifdef WITH_TLS_TRANSP } #endif /* TLS_TRANSP */ } #ifdef RAW_SUPPORT else { /* lets check if the ICMP message matches with our sent packet */ flen = sizeof(faddr); memset(&faddr, 0, sizeof(struct sockaddr)); ret = recvfrom(rawsock, buf, size, 0, (struct sockaddr *)&faddr, &flen); if (ret == -1) { perror("error while trying to read from icmp raw socket"); exit_code(2, __PRETTY_FUNCTION__, "failed to read from ICMP RAW socket"); } r_ip_hdr = (struct ip *) buf; r_ip_len = r_ip_hdr->ip_hl << 2; icmp_hdr = (struct icmp *) (buf + r_ip_len); icmp_len = ret - r_ip_len; if (icmp_len < 8) { if (verbose > 1) printf(": ignoring (ICMP header length below 8 bytes)\n"); return -2; } else if (icmp_len < 36) { if (verbose > 1) printf(": ignoring (ICMP message too short to contain IP and UDP header)\n"); return -2; } s_ip_hdr = (struct ip *) ((char *)icmp_hdr + 8); s_ip_len = s_ip_hdr->ip_hl << 2; if (s_ip_hdr->ip_p == IPPROTO_UDP) { udp_hdr = (struct udphdr *) ((char *)s_ip_hdr + s_ip_len); srcport = ntohs(udp_hdr->uh_sport); dstport = ntohs(udp_hdr->uh_dport); dbg("\nlport: %i, rport: %i\n", cd->lport, cd->rport); if ((srcport == cd->lport) && (dstport == cd->rport)) { printf(" (type: %u, code: %u)", icmp_hdr->icmp_type, icmp_hdr->icmp_code); #ifdef HAVE_INET_NTOP if (inet_ntop(AF_INET, &faddr.sin_addr, &source_dot[0], INET_ADDRSTRLEN) != NULL) printf(": from %s\n", source_dot); else printf("\n"); #else printf("\n"); #endif // HAVE_INET_NTOP log_message(request); exit_code(3, __PRETTY_FUNCTION__, "received ICMP error"); } else { if (verbose > 2) printf(": ignoring (ICMP message does not match used ports)\n"); return -2; } } else { if (verbose > 1) printf(": ignoring (ICMP data is not a UDP packet)\n"); return -2; } } #endif // RAW_SUPPORT if (ret > 0) { *(buf+ ret) = '\0'; if (cd->transport != SIP_UDP_TRANSPORT) { if (verbose > 0) printf("\nchecking message for completeness...\n"); if (complete_mes(buf, ret) == 1) { cd->buf_tmp = NULL; ret += cd->buf_tmp_size; cd->buf_tmp_size = 0; } else { if (cd->buf_tmp) { cd->buf_tmp += ret; cd->buf_tmp_size += ret; } else { cd->buf_tmp = buf + ret; cd->buf_tmp_size = ret; } cd->dontsend = 1; ret = -1; } } /* store the biggest delay if one occurred */ if (srt->delaytime.tv_sec != 0) { tmp_delay = deltaT(&(srt->delaytime), &(srt->recvtime)); if (tmp_delay > sd->big_delay) sd->big_delay = tmp_delay; if ((sd->small_delay == 0) || (tmp_delay < sd->small_delay)) sd->small_delay = tmp_delay; srt->delaytime.tv_sec = 0; srt->delaytime.tv_usec = 0; } if (srt->timing > 0) { tmp_delay = deltaT(&(srt->sendtime), &(srt->recvtime)); if (tmp_delay > sd->big_delay) sd->big_delay = tmp_delay; if ((sd->small_delay == 0) || (tmp_delay < sd->small_delay)) sd->small_delay = tmp_delay; sd->all_delay += tmp_delay; } #ifdef HAVE_INET_NTOP if ((verbose > 2) && (getpeername(sock, (struct sockaddr *)&peer_adr, &psize) == 0) && (inet_ntop(peer_adr.sin_family, &peer_adr.sin_addr, &source_dot[0], INET_ADDRSTRLEN) != NULL)) { printf("\nreceived from: %s:%s:%i\n", transport_str, source_dot, ntohs(peer_adr.sin_port)); } else if ((verbose > 1) && (mode != SM_TRACE && mode != SM_USRLOC)) printf(":\n"); #else if (mode != SM_TRACE && mode != SM_USRLOC) printf(":\n"); #endif // HAVE_INET_NTOP if (!inv_trans && ret > 0 && (regexec(&(reg->proexp), buf, 0, 0, 0) != REG_NOERROR)) { sd->retryAfter = srt->timer_t1; } } else { check_socket_error(sock, buf, size, mode, request); printf("\nnothing received, select returned error\n"); exit_code(2, __PRETTY_FUNCTION__, "nothing received, select returned error"); } return ret; } /* clears the given sockaddr, fills it with the given data and if a * socket is given connects the socket to the new target */ int set_target(struct sockaddr_in *adr, unsigned long target, int port, int socket, int connected, unsigned int transport, char *domainname #ifdef WITH_TLS_TRANSP , int ignore_ca_fail #endif ) { #ifdef WITH_TLS_TRANSP int ret; # ifdef USE_OPENSSL int err; X509* cert; # endif /* USE_OPENSSL */ #endif /* WITH_TLS_TRANSP */ if (socket != -1 && transport != SIP_UDP_TRANSPORT && connected) { if (shutdown(socket, SHUT_RDWR) != 0) { perror("error while shutting down socket"); } } memset(adr, 0, sizeof(struct sockaddr_in)); adr->sin_addr.s_addr = target; adr->sin_port = htons((short)port); adr->sin_family = AF_INET; #ifdef HAVE_INET_NTOP inet_ntop(adr->sin_family, &adr->sin_addr, &target_dot[0], INET_ADDRSTRLEN); #endif if (socket != -1) { if (connect(socket, (struct sockaddr *)adr, sizeof(struct sockaddr_in)) == -1) { perror("connecting socket failed"); exit_code(2, __PRETTY_FUNCTION__, "connecting socket failed"); } #ifdef WITH_TLS_TRANSP if (transport == SIP_TLS_TRANSPORT) { # ifdef USE_GNUTLS # ifdef HAVE_GNUTLS_319 gnutls_handshake_set_timeout(tls_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); # endif ret = gnutls_handshake(tls_session); if (ret < 0) { dbg("TLS Handshake FAILED!!!\n"); gnutls_perror(ret); exit_code(3, __PRETTY_FUNCTION__, "TLS handshake failed"); } else if (verbose > 2) { dbg(" TLS Handshake was completed!\n"); gnutls_session_info(tls_session); if (verify_certificate_simple(tls_session, domainname, ignore_ca_fail) != 0) { if (ignore_ca_fail == 1) { if (verbose) { printf("WARN: Ignoring verification failures of the server certificate\n"); } } else { if (verbose > 1) { printf("TLS server certificate verification can be ignored with option --tls-ignore-cert-failure.\n"); } exit_code(3, __PRETTY_FUNCTION__, "failure during TLS server certificate verification"); } } //verify_certificate_chain(tls_session, domainname, cert_chain, cert_chain_length); } # else /* USE_GNUTLS */ # ifdef USE_OPENSSL ret = SSL_connect(ssl); if (ret == 1) { dbg("TLS connect successful\n"); if (verbose > 2) { printf("TLS connect: new connection using %s %s %d\n", SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl), SSL_get_cipher_bits(ssl, 0)); } cert = SSL_get_peer_certificate(ssl); if (cert != 0) { tls_dump_cert_info("TLS connect: server certificate", cert); if (SSL_get_verify_result(ssl) != X509_V_OK) { perror("TLS connect: server certificate verification failed!!!\n"); exit_code(3, __PRETTY_FUNCTION__, "TLS server certificate verification falied"); } X509_free(cert); } else { perror("TLS connect: server did not present a certificate\n"); exit_code(3, __PRETTY_FUNCTION__, "missing TLS server certificate"); } } else { err = SSL_get_error(ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: perror("TLS handshake failed cleanly'n"); break; case SSL_ERROR_WANT_READ: perror("Need to get more data to finish TLS connect\n"); break; case SSL_ERROR_WANT_WRITE: perror("Need to send more data to finish TLS connect\n"); break; #if OPENSSL_VERSION_NUMBER >= 0x00907000L /* 0.9.7 */ case SSL_ERROR_WANT_CONNECT: perror("Need to retry connect\n"); break; case SSL_ERROR_WANT_ACCEPT: perror("Need to retry accept'n"); break; #endif /* 0.9.7 */ case SSL_ERROR_WANT_X509_LOOKUP: perror("Application callback asked to be called again\n"); break; case SSL_ERROR_SYSCALL: printf("TLS connect: %d\n", err); if (!err) { if (ret == 0) { perror("Unexpected EOF occurred while performing TLS connect\n"); } else { printf("IO error: (%d) %s\n", errno, strerror(errno)); } } break; default: printf("TLS error: %d\n", err); } exit_code(2, __PRETTY_FUNCTION__, "generic SSL error"); } # endif /* USE_OPENSSL */ # endif /* USE_GNUTLS */ } #endif /* WITH_TLS_TRANSP */ } return 1; } sipsak-0.9.8.1/src/transport.h000066400000000000000000000045521377576723000162040ustar00rootroot00000000000000/* * Copyright (C) 2005 Nils Ohlmeier * * This file belongs to sipsak, a free sip testing tool. * * sipsak is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * sipsak is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef SIPSAK_TRANSPORT_H #define SIPSAK_TRANSPORT_H #include "sipsak.h" #include "shoot.h" #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif /* TIME_WITH_SYS_TIME */ #ifdef HAVE_SYS_SOCKET_H # include #endif struct sipsak_sr_time { struct timeval sendtime; struct timeval recvtime; struct timeval firstsendt; struct timeval starttime; struct timeval delaytime; int timer_t1; int timer_t2; int timer_final; int timing; }; struct sipsak_con_data { struct sockaddr_in adr; int csock; int usock; int dontsend; int dontrecv; int connected; int symmetric; int lport; int rport; unsigned int transport; char *buf_tmp; int buf_tmp_size; }; struct sipsak_counter { int send_counter; int retrans_r_c; int retrans_s_c; int randretrys; int run; int namebeg; int nameend; }; struct sipsak_delay { int retryAfter; double big_delay; double small_delay; double all_delay; }; extern char *transport_str; void init_network(struct sipsak_con_data *cd, char *local_ip #ifdef WITH_TLS_TRANSP , char *ca_file #endif ); void shutdown_network(); void send_message(char* mes, struct sipsak_con_data *cd, struct sipsak_counter *sc, struct sipsak_sr_time *srt); int recv_message(char *buf, int size, int inv_trans, struct sipsak_delay *sd, struct sipsak_sr_time *srt, struct sipsak_counter *count, struct sipsak_con_data *cd, struct sipsak_regexp *reg, enum sipsak_modes mode, int cseq_counter, char *request, char *response); int set_target(struct sockaddr_in *adr, unsigned long target, int port, int socket, int connected, unsigned int transport, char *domainname #ifdef WITH_TLS_TRANSP , int ignore_ca_fail #endif ); #endif sipsak-0.9.8.1/tests/000077500000000000000000000000001377576723000143445ustar00rootroot00000000000000sipsak-0.9.8.1/tests/check_auth.c000066400000000000000000000167161377576723000166210ustar00rootroot00000000000000#include "../config.h" #include #ifdef HAVE_CHECK_H #include #include "../src/auth.h" #include "../src/sipsak.h" #define RUNNING_CHECK 1 void shutdown_network() {}; int verbose = 99; int expected_exit_code = -1; char *expected_exit_reason = NULL; int expected_exit_code_called = 0; void set_expected_exit_code(int code, const char *reason) { expected_exit_code = code; expected_exit_reason = reason; expected_exit_code_called = 0; } void exit_code(int code, const char *function, const char *reason) { if (expected_exit_code == -1) { ck_abort_msg("Unexpected call to exit_code() with code %i at %s: %s", code, function, reason); } expected_exit_code_called = 1; ck_assert_msg(expected_exit_code == code, "Expected call to exit_code() with wrong code number"); ck_assert_str_eq(expected_exit_reason, reason); }; START_TEST (test_insert_auth_md5) { char message1[BUFSIZE] = "REGISTER test@example.org SIP/2.0\r\nbarfoo\r\n"; char auth_response[BUFSIZE] = "401\r\nWWW-Authenticate: Digest algorithm=MD5,nonce=1234567890,realm=example.org\r\n"; char username[] = "testuser"; char password[] = "helloworld"; const char expected1[] = "REGISTER test@example.org SIP/2.0\r\nAuthorization: Digest username=\"testuser\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, nonce=1234567890, response=\"6a1ad69841f79661cda113d0dff8ab3d\"\r\nbarfoo\r\n"; insert_auth(&message1[0], &auth_response[0], &username[0], &password[0], NULL, NULL, 0, 0); ck_assert_msg(strcmp(&message1[0], &expected1[0]) == 0, "insert_auth() 'basic' resulted in '%s' instead of '%s'", &message1[0], &expected1[0]); char message2[BUFSIZE] = "INVITE test@example.org SIP/2.0\r\nbarfoo\r\n"; char auth_username[] = "authuser"; const char expected2[] = "INVITE test@example.org SIP/2.0\r\nAuthorization: Digest username=\"authuser\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, nonce=1234567890, response=\"97b2d3f13a5484a471c681faf7ca14b1\"\r\nbarfoo\r\n"; insert_auth(&message2[0], &auth_response[0], NULL, &password[0], &auth_username[0], NULL, 0, 0); ck_assert_msg(strcmp(&message2[0], &expected2[0]) == 0, "insert_auth() 'authusername' resulted in '%s' instead of '%s'", &message2[0], &expected2[0]); char message3[BUFSIZE] = "MESSAGE test@example.org SIP/2.0\r\nbarfoo\r\n"; const char expected3[] = "MESSAGE test@example.org SIP/2.0\r\nAuthorization: Digest username=\"testuser1\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, nonce=1234567890, response=\"dcb615fe24c81241d6fa0c533d153e51\"\r\nbarfoo\r\n"; insert_auth(&message3[0], &auth_response[0], &username[0], &password[0], NULL, NULL, 1, 1); ck_assert_msg(strcmp(&message3[0], &expected3[0]) == 0, "insert_auth() 'namebegin' resulted in '%s' instead of '%s'", &message3[0], &expected3[0]); char message4[BUFSIZE] = "foobar test@example.org SIP/2.0\r\nbarfoo\r\n"; char authhash[] = "dcb615fe24c81241d6fa0c533d153e51"; const char expected4[] = "foobar test@example.org SIP/2.0\r\nAuthorization: Digest username=\"testuser\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, nonce=1234567890, response=\"48d6b710677a1aca3a5424debf9e012a\"\r\nbarfoo\r\n"; insert_auth(&message4[0], &auth_response[0], &username[0], &password[0], NULL, &authhash[0], 0, 0); ck_assert_msg(strcmp(&message4[0], &expected4[0]) == 0, "insert_auth() 'authhash' resulted in '%s' instead of '%s'", &message4[0], &expected4[0]); char message5[BUFSIZE] = "ACK test@example.org SIP/2.0\r\nbarfoo\r\n"; char auth_response2[BUFSIZE] = "401\r\nWWW-Authenticate: Digest algorithm=MD5, realm=example.org, nonce=1234567890, opaque=sjw8fn3wbj5sfs,\r\n"; const char expected5[] = "ACK test@example.org SIP/2.0\r\nAuthorization: Digest username=\"testuser\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, opaque=sjw8fn3wbj5sfs, nonce=1234567890, response=\"781a7065cb6c4ecba6fdccd0ef8190c5\"\r\nbarfoo\r\n"; insert_auth(&message5[0], &auth_response2[0], &username[0], &password[0], NULL, NULL, 0, 0); ck_assert_msg(strcmp(&message5[0], &expected5[0]) == 0, "insert_auth() 'opaque' resulted in '%s' instead of '%s'", &message5[0], &expected5[0]); /* char message6[BUFSIZE] = "INVITE test@example.org SIP/2.0\r\nbarfoo\r\n"; char auth_response3[BUFSIZE] = "401\r\nWWW-Authenticate: Digest algorithm=MD5, realm=example.org, qop=\"auth\", nonce=1234567890\r\n"; const char expected6[] = "INVITE test@example.org SIP/2.0\r\nAuthorization: Digest username=\"testuser\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, nonce=1234567890, qop=auth, nc=00000001, cnonce=\"41a7\", response=\"78e1c78a0638d5e25fe430fb3e7946f8\"\r\nbarfoo\r\n"; insert_auth(&message6[0], &auth_response3[0], &username[0], &password[0], NULL, NULL, 0, 0); ck_assert_msg(strcmp(&message6[0], &expected6[0]) == 0, "insert_auth() 'qop=auth' resulted in '%s' instead of '%s'", &message6[0], &expected6[0]); char message7[BUFSIZE] = "INVITE test@example.org SIP/2.0\r\nbarfoo\r\n"; char auth_response4[BUFSIZE] = "401\r\nWWW-Authenticate: Digest algorithm=MD5, realm=example.org, opaque=vdjn5t8gvsjs,qop=\"auth\", nonce=1234567890\r\n"; const char expected7[] = "INVITE test@example.org SIP/2.0\r\nAuthorization: Digest username=\"testuser\", uri=\"test@example.org\", algorithm=MD5, realm=example.org, opaque=vdjn5t8gvsjs, nonce=1234567890, qop=auth, nc=00000002, cnonce=\"10d63af1\", response=\"78e43bf1d1aa928f701dcc8b3ab9aa97\"\r\nbarfoo\r\n"; insert_auth(&message7[0], &auth_response4[0], &username[0], &password[0], NULL, NULL, 0, 0); ck_assert_msg(strcmp(&message7[0], &expected7[0]) == 0, "insert_auth() 'opaque + qop' resulted in '%s' instead of '%s'", &message7[0], &expected7[0]); */ } END_TEST START_TEST (test_insert_auth_exits) { char message1[BUFSIZE] = "REGISTER test@example.org SIP/2.0\r\nbarfoo\r\n"; //char auth_response[BUFSIZE] = "401\r\nWWW-Authenticate: Digest algorithm=MD5,nonce=1234567890,realm=example.org\r\n"; char auth_response[BUFSIZE] = "401"; char username[] = "testuser"; char password[] = "helloworld"; set_expected_exit_code(3, "missing authentication header in reply"); insert_auth(&message1[0], &auth_response[0], &username[0], &password[0], NULL, NULL, 0, 0); ck_assert_int_eq(expected_exit_code_called, 1); /* char message2[BUFSIZE] = "REGISTER"; set_expected_exit_code(3, "missing new line in request"); insert_auth(&message2[0], &auth_response[0], &username[0], &password[0], NULL, NULL, 0, 0); ck_assert_int_eq(expected_exit_code_called, 1); */ } END_TEST Suite *auth_suite(void) { Suite *s = suite_create("auth"); TCase *tc_insert_auth_md5 = tcase_create("test_insert_auth_md5"); tcase_add_test(tc_insert_auth_md5, test_insert_auth_md5); TCase *tc_insert_auth_exits = tcase_create("test_insert_auth_exits"); tcase_add_test(tc_insert_auth_exits, test_insert_auth_exits); /* add test cases to suite */ suite_add_tcase(s, tc_insert_auth_md5); suite_add_tcase(s, tc_insert_auth_exits); return s; } int main(void) { int number_failed; Suite *s = auth_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #else /* HAVE_CHECK_H */ #include int main(void) { printf("check_auth: !!! missing check unit test framework !!!\n"); return EXIT_FAILURE; } #endif /* HAVE_CHECK_H */ sipsak-0.9.8.1/tests/check_helper.c000066400000000000000000000267341377576723000171400ustar00rootroot00000000000000#include "../config.h" #include #ifdef HAVE_CHECK_H #include #include "../src/helper.h" #define RUNNING_CHECK 1 void shutdown_network() {}; int verbose = 99; void exit_code(int code, const char *function, const char *reason) { ck_abort_msg("Unexpected call to exit_code() with code %i at %s: %s", code, function, reason); }; START_TEST (test_is_number) { /* failure cases */ ck_assert_msg(is_number("") == 0, "is_number(\"\") returned %d, instead of 0", is_number("")); ck_assert_msg(is_number("a") == 0, "is_number(\"a\") returned %d, instead of 0", is_number("a")); ck_assert_msg(is_number("XYZ") == 0, "is_number(\"XYZ\") returned %d, instead of 0", is_number("XYZ")); ck_assert_msg(is_number("10i") == 0, "is_number(\"10i\") returned %d, instead of 0", is_number("10i")); ck_assert_msg(is_number("p01") == 0, "is_number(\"p01\") returned %d, instead of 0", is_number("p01")); ck_assert_msg(is_number("p2q") == 0, "is_number(\"p2q\") returned %d, instead of 0", is_number("p2q")); ck_assert_msg(is_number("1b3") == 0, "is_number(\"1b3\") returned %d, instead of 0", is_number("1b3")); /* success cases */ ck_assert_msg(is_number("1") == 1, "is_number(\"1\") returned %d, instead of 1", is_number("1")); ck_assert_msg(is_number("10") == 1, "is_number(\"10\") returned %d, instead of 1", is_number("10")); } END_TEST START_TEST (test_str_to_int) { /* failure because empty */ ck_assert_msg(str_to_int(0, "") == -2, "str_to_int(0, \"\") returned %d, instead of -2", str_to_int(0, "")); ck_assert_msg(str_to_int(1, "") == -2, "str_to_int(1, \"\") returned %d, instead of -2", str_to_int(1, "")); ck_assert_msg(str_to_int(0, " ") == -2, "str_to_int(0, \" \") returned %d, instead of -2", str_to_int(0, " ")); ck_assert_msg(str_to_int(1, " ") == -2, "str_to_int(1, \" \") returned %d, instead of -2", str_to_int(1, " ")); ck_assert_msg(str_to_int(0, " ") == -2, "str_to_int(0, \"\\t\") returned %d, instead of -2", str_to_int(0, " ")); ck_assert_msg(str_to_int(1, " ") == -2, "str_to_int(1, \"\\t\") returned %d, instead of -2", str_to_int(1, " ")); ck_assert_msg(str_to_int(0, " ") == -2, "str_to_int(0, \" \\t\") returned %d, instead of -2", str_to_int(0, " ")); ck_assert_msg(str_to_int(1, " ") == -2, "str_to_int(1, \" \\t\") returned %d, instead of -2", str_to_int(1, " ")); /* failure because non-int */ ck_assert_msg(str_to_int(0, "a") == -2, "str_to_int(0, \"a\") returned %d, instead of -2", str_to_int(0, "a")); ck_assert_msg(str_to_int(1, "a") == -2, "str_to_int(1, \"a\") returned %d, instead of -2", str_to_int(1, "a")); ck_assert_msg(str_to_int(0, " a") == -2, "str_to_int(0, \" a\") returned %d, instead of -2", str_to_int(0, " a")); ck_assert_msg(str_to_int(1, " a") == -2, "str_to_int(1, \" a\") returned %d, instead of -2", str_to_int(1, " a")); ck_assert_msg(str_to_int(0, " a ") == -2, "str_to_int(0, \" a \") returned %d, instead of -2", str_to_int(0, " a ")); ck_assert_msg(str_to_int(1, " a ") == -2, "str_to_int(1, \" a \") returned %d, instead of -2", str_to_int(1, " a ")); ck_assert_msg(str_to_int(0, "ABC") == -2, "str_to_int(0, \"ABC\") returned %d, instead of -2", str_to_int(0, "ABC")); ck_assert_msg(str_to_int(1, "ABC") == -2, "str_to_int(1, \"ABC\") returned %d, instead of -2", str_to_int(1, "ABC")); ck_assert_msg(str_to_int(0, " ABC") == -2, "str_to_int(0, \" ABC\") returned %d, instead of -2", str_to_int(0, " ABC")); ck_assert_msg(str_to_int(1, " ABC") == -2, "str_to_int(1, \" ABC\") returned %d, instead of -2", str_to_int(1, " ABC")); ck_assert_msg(str_to_int(0, " ABC ") == -2, "str_to_int(0, \" ABC\\t\") returned %d, instead of -2", str_to_int(0, " ABC ")); ck_assert_msg(str_to_int(1, " ABC ") == -2, "str_to_int(1, \" ABC\\t\") returned %d, instead of -2", str_to_int(1, " ABC ")); /* success cases */ ck_assert_msg(str_to_int(0, "1") == 1, "str_to_int(0, \"1\") returned %d, instead of 1", str_to_int(0, "1")); ck_assert_msg(str_to_int(1, "1") == 1, "str_to_int(1, \"1\") returned %d, instead of 1", str_to_int(1, "1")); ck_assert_msg(str_to_int(0, "10") == 10, "str_to_int(0, \"10\") returned %d, instead of 10", str_to_int(0, "10")); ck_assert_msg(str_to_int(1, "10") == 10, "str_to_int(1, \"10\") returned %d, instead of 10", str_to_int(1, "10")); ck_assert_msg(str_to_int(0, " 10") == 10, "str_to_int(0, \" 10\") returned %d, instead of 10", str_to_int(0, " 10")); ck_assert_msg(str_to_int(1, " 10") == 10, "str_to_int(1, \" 10\") returned %d, instead of 10", str_to_int(1, " 10")); ck_assert_msg(str_to_int(0, " 10 ") == 10, "str_to_int(0, \" 10 \") returned %d, instead of 10", str_to_int(0, " 10 ")); ck_assert_msg(str_to_int(1, " 10 ") == 10, "str_to_int(1, \" 10 \") returned %d, instead of 10", str_to_int(1, " 10 ")); ck_assert_msg(str_to_int(0, " 10 ") == 10, "str_to_int(0, \"\\t 10 \\t\") returned %d, instead of 10", str_to_int(0, " 10 ")); ck_assert_msg(str_to_int(1, " 10 ") == 10, "str_to_int(1, \"\\t 10 \\t\") returned %d, instead of 10", str_to_int(1, " 10 ")); /* success and failures depending on the mode */ ck_assert_msg(str_to_int(0, "1 a") == -2, "str_to_int(0, \"1 a\") returned %d, instead of -2", str_to_int(0, "1 a")); ck_assert_msg(str_to_int(1, "1 a") == 1, "str_to_int(1, \"1 a\") returned %d, instead of 1", str_to_int(1, "1")); ck_assert_msg(str_to_int(0, "10 B") == -2, "str_to_int(0, \"10\\tB\") returned %d, instead of -2", str_to_int(0, "10 B")); ck_assert_msg(str_to_int(1, "10 B") == 10, "str_to_int(1, \"10\\tB\") returned %d, instead of 10", str_to_int(1, "10 B")); ck_assert_msg(str_to_int(0, " 100 ABC ") == -2, "str_to_int(0, \" 100\\tABC \") returned %d, instead of -2", str_to_int(0, " 100 ABC ")); ck_assert_msg(str_to_int(1, " 100 ABC ") == 100, "str_to_int(1, \" 100\\tABC \") returned %d, instead of 100", str_to_int(1, " 100 ABC ")); } END_TEST START_TEST (test_is_ip) { /* failure cases */ ck_assert_msg(is_ip("") == 0, "is_ip(\"\") returned %d, instead of 0", is_ip("")); ck_assert_msg(is_ip("0") == 0, "is_ip(\"0\") returned %d, instead of 0", is_ip("0")); ck_assert_msg(is_ip("100") == 0, "is_ip(\"100\") returned %d, instead of 0", is_ip("100")); ck_assert_msg(is_ip("1000") == 0, "is_ip(\"1000\") returned %d, instead of 0", is_ip("1000")); ck_assert_msg(is_ip("1.0") == 0, "is_ip(\"1.0\") returned %d, instead of 0", is_ip("1.0")); ck_assert_msg(is_ip("1.2.0") == 0, "is_ip(\"1.2.0\") returned %d, instead of 0", is_ip("1.2.0")); ck_assert_msg(is_ip("1.2.3.4.5") == 0, "is_ip(\"1.2.3.4.5\") returned %d, instead of 0", is_ip("1.2.3.4.5")); ck_assert_msg(is_ip("1000.0.0.0") == 0, "is_ip(\"1000.0.0.0\") returned %d, instead of 0", is_ip("1000.0.0.0")); ck_assert_msg(is_ip("0.1000.0.0") == 0, "is_ip(\"0.1000.0.0\") returned %d, instead of 0", is_ip("0.1000.0.0")); ck_assert_msg(is_ip("0.0.1000.0") == 0, "is_ip(\"0.0.1000.0\") returned %d, instead of 0", is_ip("0.0.1000.0")); ck_assert_msg(is_ip("0.0.0.1000") == 0, "is_ip(\"0.0.0.1000\") returned %d, instead of 0", is_ip("0.0.0.1000")); /* success cases */ ck_assert_msg(is_ip("0.0.0.0") == 1, "is_ip(\"0.0.0.0\") returned %d, instead of 1", is_ip("0.0.0.0")); ck_assert_msg(is_ip("1.2.3.4") == 1, "is_ip(\"1.2.3.4\") returned %d, instead of 1", is_ip("1.2.3.4")); ck_assert_msg(is_ip("192.168.1.1") == 1, "is_ip(\"192.168.1.1\") returned %d, instead of 1", is_ip("192.168.1.1")); /* this is a known limitation ;) */ ck_assert_msg(is_ip("999.999.999.999") == 1, "is_ip(\"999.999.999.999\") returned %d, instead of 1", is_ip("999.999.999.999")); } END_TEST START_TEST (test_getaddress) { unsigned long localaddr; /* failure case */ ck_assert_msg(getaddress("") == 0, "getaddress(\"\") returned %lu, instead of 0", getaddress("")); localaddr = htonl(0x7f000001L); /* success cases */ ck_assert_msg(getaddress("127.0.0.1") == localaddr, "getaddress(\"127.0.0.1\") returned %lu, instead of %lu", getaddress("127.0.0.1"), localaddr); /* this should work also without DNS */ ck_assert_msg(getaddress("localhost") == localaddr, "getaddress(\"localhost\") returned %lu, instead of %lu", getaddress("localhost"), localaddr); } END_TEST START_TEST (test_insert_cr) { char ta[15]; memset(ta, '\0', 15); insert_cr(ta); ck_assert_msg(memcmp(ta, "\r\n\0\0\0\0\0\0\0\0\0\0\0\0\0", 15) == 0, "insert_cr(\"\") returned '%s', instead of \"\r\n\"", ta); memset(ta, '\0', 15); memcpy(ta, "test", 4); insert_cr(ta); ck_assert_msg(memcmp(ta, "test\r\n\0\0\0\0\0\0\0\0\0", 15) == 0, "insert_cr(\"test\") returned '%s', instead of \"test\r\n\"", ta); memset(ta, '\0', 15); memcpy(ta, "test\n", 5); insert_cr(ta); ck_assert_msg(memcmp(ta, "test\r\n\r\n\0\0\0\0\0\0\0", 15) == 0, "insert_cr(\"test\\n\") returned '%s', instead of \"test\\r\\n\"", ta); memset(ta, '\0', 15); memcpy(ta, "foo\nbar\n", 8); insert_cr(ta); ck_assert_msg(memcmp(ta, "foo\r\nbar\r\n\r\n\0\0\0", 15) == 0, "insert_cr(\"foo\\nbar\\n\") returned '%s', instead of \"foo\\r\\nbar\\r\\n\"", ta); } END_TEST START_TEST (test_get_fqdn) { char fqdn[FQDN_SIZE]; memset(fqdn, '\0', FQDN_SIZE); get_fqdn(fqdn, 0, "127.0.0.15"); ck_assert_msg(memcmp(fqdn, "127.0.0.15\0", 11) == 0, "get_fqdn returned '%s', instead of '127.0.0.15'", fqdn); memset(fqdn, '\0', FQDN_SIZE); get_fqdn(fqdn, 0, "localhost"); ck_assert_msg(memcmp(fqdn, "localhost\0", 10) == 0, "get_fqdn returned '%s', instead of 'localhost'", fqdn); memset(fqdn, '\0', FQDN_SIZE); get_fqdn(fqdn, 1, "127.0.0.21"); ck_assert_msg(memcmp(fqdn, "127.0.0.21\0", 11) == 0, "get_fqdn returned '%s', instead of '127.0.0.21'", fqdn); memset(fqdn, '\0', FQDN_SIZE); get_fqdn(fqdn, 1, "localhost"); ck_assert_msg(memcmp(fqdn, "127.0.0.1\0", 10) == 0, "get_fqdn returned '%s', instead of '127.0.0.1'", fqdn); /* this fails on Travis Linux hosts, because their host names have no dots memset(fqdn, '\0', FQDN_SIZE); get_fqdn(fqdn, 0, 0); ck_assert_msg(memcmp(fqdn, "\0\0\0\0\0", 5) != 0, "get_fqdn empty buffer '%s'", fqdn); fprintf(stderr, "after fqdn test 1\n"); memset(fqdn, '\0', FQDN_SIZE); get_fqdn(fqdn, 1, 0); ck_assert_msg(memcmp(fqdn, "127.0.0.1\0", 10) == 0, "get_fqdn returned '%s', instead of '127.0.0.1'", fqdn); fprintf(stderr, "after fqdn test 2\n"); */ } END_TEST Suite *helper_suite(void) { Suite *s = suite_create("Helper"); /* is_number test case */ TCase *tc_is_number = tcase_create("is_number"); tcase_add_test(tc_is_number, test_is_number); /* str_to_int test case */ TCase *tc_str_to_int = tcase_create("str_to_int"); tcase_add_test(tc_str_to_int, test_str_to_int); /* is_ip test case */ TCase *tc_is_ip = tcase_create("is_ip"); tcase_add_test(tc_is_ip, test_is_ip); /* getaddress test case */ TCase *tc_getaddress = tcase_create("getaddress"); tcase_add_test(tc_getaddress, test_getaddress); /* insert_cr test case */ TCase *tc_insert_cr = tcase_create("insert_cr"); tcase_add_test(tc_insert_cr, test_insert_cr); /* get_fqdn test case */ TCase *tc_get_fqdn = tcase_create("get_fqdn"); tcase_add_test(tc_get_fqdn, test_get_fqdn); /* add test cases to suite */ suite_add_tcase(s, tc_is_number); suite_add_tcase(s, tc_str_to_int); suite_add_tcase(s, tc_is_ip); suite_add_tcase(s, tc_getaddress); suite_add_tcase(s, tc_insert_cr); suite_add_tcase(s, tc_get_fqdn); return s; } int main(void) { int number_failed; Suite *s = helper_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #else /* HAVE_CHECK_H */ #include int main(void) { printf("check_helper: !!! missing check unit test framework !!!\n"); return EXIT_FAILURE; } #endif /* HAVE_CHECK_H */ sipsak-0.9.8.1/tests/check_md5.c000066400000000000000000000040251377576723000163330ustar00rootroot00000000000000#include "../config.h" #include #ifdef HAVE_CHECK_H #include #include "../src/md5.h" #define RUNNING_CHECK 1 char md5hex_buf[33]; /* NULed by initialization */ const char *md5hex(unsigned char res[16]) { int i; for (i = 0; i < 16; ++i) { sprintf(md5hex_buf + 2 * i, "%02hhx", res[i]); } return md5hex_buf; } START_TEST (test_md5_empty) { const char expected[] = "d41d8cd98f00b204e9800998ecf8427e"; unsigned char res[16]; MD5_CTX ctx; MD5Init(&ctx); MD5Final(&res[0], &ctx); ck_assert_msg( strcmp(md5hex(res), expected) == 0, "md5('') returned %s instead of %s", &md5hex_buf[0], expected); } END_TEST START_TEST (test_md5_quick_brown_fox) { const char expected[] = "14aa0efdbdac9187f334b9d25ddeaefe"; unsigned char res[16]; MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, "The quick brown fox ", 20); MD5Update(&ctx, "jumped over ", 12); MD5Update(&ctx, "the \0NUL\n", 9); //MD5Update(&ctx, "The quick brown fox jumped over the \0NUL\n", 41); MD5Final(&res[0], &ctx); ck_assert_msg( strcmp(md5hex(res), expected) == 0, "md5('The quick brown fox jumped over the \\0NUL\\n') " "returned %s instead of %s", &md5hex_buf[0], expected); } END_TEST Suite *md5_suite(void) { Suite *s = suite_create("MD5"); TCase *tc_md5_empty = tcase_create("test_md5_empty"); tcase_add_test(tc_md5_empty, test_md5_empty); TCase *tc_md5_quick_brown_fox = tcase_create("test_md5_quick_brown_fox"); tcase_add_test(tc_md5_quick_brown_fox, test_md5_quick_brown_fox); /* add test cases to suite */ suite_add_tcase(s, tc_md5_empty); suite_add_tcase(s, tc_md5_quick_brown_fox); return s; } int main(void) { int number_failed; Suite *s = md5_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_VERBOSE); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #else /* HAVE_CHECK_H */ #include int main(void) { printf("check_md5: !!! missing check unit test framework !!!\n"); return EXIT_FAILURE; } #endif /* HAVE_CHECK_H */