fixed a serious bug in wafp which causes wafp to ignore the base dirs
provided by the user in the url.
e.g. if a user provided http://www.example.com/lulz/ , wafp ignored /lulz/
and fetched the fingerprinting files from http://www.example.com/.
that caused huge trouble while fingerprintg apps which are not located
within the doc-root.
http://code.google.com/p/webapplicationfingerprinter/source/detail?r=5
Added:
/branches/e-axe_redirecting
/branches/e-axe_redirecting/CREDITS
/branches/e-axe_redirecting/HOWTO
/branches/e-axe_redirecting/LICENSE
/branches/e-axe_redirecting/README
/branches/e-axe_redirecting/fprints_wafp.db
/branches/e-axe_redirecting/lib
/branches/e-axe_redirecting/lib/fprints_wafp.schema
/branches/e-axe_redirecting/lib/scan_wafp.schema
/branches/e-axe_redirecting/lib/wafp_http.rb
/branches/e-axe_redirecting/lib/wafp_https.rb
/branches/e-axe_redirecting/lib/wafp_pidify.rb
/branches/e-axe_redirecting/scan_wafp.db
/branches/e-axe_redirecting/utils
/branches/e-axe_redirecting/utils/README.generate_wafp_fingerprint
/branches/e-axe_redirecting/utils/SUBMIT_YOUR_FPS
/branches/e-axe_redirecting/utils/extract_from_db.sh
/branches/e-axe_redirecting/utils/generate_fp_sync_list.sh
/branches/e-axe_redirecting/utils/generate_wafp_fingerprint.sh
/branches/e-axe_redirecting/utils/online_update.sh
/branches/e-axe_redirecting/wafp.rb
Modified:
/trunk/wafp.rb
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/CREDITS Sat Jan 23 13:51:30 2010
@@ -0,0 +1,13 @@
+Thanks to
+
+ - Oliver Karow
+
+...for intensive testing, feature requests and bugreporting,
+
+ - Thilo `the cool` Bebber
+
+...for intensive investigation in the texts driving the documentation and
website,
+
+ - Michael White
+
+...for the quick OSX readiness check.
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/HOWTO Sat Jan 23 13:51:30 2010
@@ -0,0 +1,107 @@
+ WAFP
+ (c) Richard Sammet (e-axe)
+ http://mytty.org/wafp/
+
+
+HOWTO use WAFP the right way?
+-----------------------------
+
+This is a guide on how to use WAFP (Web Application Finger Printer) the
right way.
+I will explain the most important features and show some of the most
useful usage scenarios:
+
+1) Standard WAFP usage
+2) Advanced WAFP usage
+3) Online Updates
+
+1.) Standard WAFP usage
+--
+If you want to find out the detailed version of a Web Application thats
not running on
+a server you have shell access to, you can use WAFP in its standard Finger
Printing mode:
+
+Targeted Site: http://www.example.com
+Targeted Application: /board
+
+Out of the Style and/or file/dir structure you saw by browsing the board
+you know it needs to be some version of phpBB.
+
+Now you can fire up WAFP like this:
+
+./wafp.rb -p phpBB http://www.example.com/board
+
+Thats it - within a few seconds to minutes (depending on your and the
targeted sites
+connection speed and your machines computing power) you will get the
detailed version
+information for the phpBB installation running on http://www.example.com
if the right
+Finger Prints are stored in you local WAFP DB.
+
+If you do not know which product is driving the website, you can start
WAFP in
+its auto identify mode which is the default:
+
+./wafp.rb http://www.example.com/board
+
+This way WAFP will try to identify the product before starting the detailed
+version fingerprinting.
+
+
+2.) Advanced WAFP usage
+--
+Now that you know on how to use it the easy way, lets have a look at some
of
+its "advanced" features.
+
+Let's assume that you are facing a site and you have no fucking clue which
Web Application is
+running there and WAFP, in its auto identify mode was also not able to
identify it.
+Well, yes, you can tell WAFP to try to identify the Web Application for
you - if WAFP have
+at least one Finger Print for one version of that Web Application in its
database, its most likely
+that it will be able to give you some result from which you can see which
Web Application is running there.
+
+Do it like that:
+
+./wafp.rb --any http://www.example.com
+
+WAFP will try to grab all files for all products and versions it knows (it
will fetch
+every file just once, even if the file exists in multiple Finger Prints)
and based
+on these information it will report the best matches.
+
+And, if you do not want to perform all the processing at once, you can
tell WAFP to just
+download and store the files information inside the scanning DB:
+
+./wafp.rb --any --fetch --store IMBA_FPRINT http://www.example.com
+
+Later on, maybe on another, much faster machine, you can start WAFP in the
dry run mode
+to process and analyse the collected data:
+
+./wafp.rb --dry IMBA_FPRINT
+
+You can also add some product and version information when using the dry
run mode:
+
+./wafp.rb -p wordpress -v '2.%' --dry IMBA_FPRINT
+
+This tells WAFP to only analyse the collected data against products
matching "wordpress"
+with a version starting with "2.".
+
+If you are facing a Website which requires Basic-Auth you can just add the
credentials
+to the URL:
+
+./wafp.rb http://username:pass...@www.example.com
+
+
+3.) Online Updates
+--
+Yes, you can update your local version of the Finger Print database from
the most
+up to date version of the master database online.
+
+The updates are done in an incremental way - that means, if you are just
missing three
+Finger Prints, than only those three Finger Prints are going to be
downloaded and applied
+to your database.
+
+If you want to see if you are running the latest DB version:
+
+./online_update.sh --show
+
+This will show you which Finger Prints would be downloaded and installed -
if there are any missing.
+
+./online_update.sh --update
+
+Performs the actual update. WAFP itself needs to be updated manualy.
+
+
+Visit http://mytty.org/wafp for more information!
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/LICENSE Sat Jan 23 13:51:30 2010
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange;
or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free
Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
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.
+
+ <signature of Ty Coon>, 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.
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/README Sat Jan 23 13:51:30 2010
@@ -0,0 +1,94 @@
+ WAFP
+ (c) Richard Sammet (e-axe)
+ http://mytty.org/wafp/
+
+
+INTRODUCTION
+------------
+WAFP is a Web Application Finger Printer written in ruby using a SQLite3
DB.
+
+How it works?
+--
+WAFP fetches the files given by the Finger Prints from a webserver and
+checks if the checksums of those files are matching to the given checksums
from the
+Finger Prints. This way it is able to detect the detailed version and
+even the build number of a Web Application.
+
+In detail?
+--
+A Web Application Finger Print consits of a set of relative file locations
+in conjunction with their md5sums. It is made based on a production or
example
+installation of a Web Application or just out of an extracted Web
Application
+install files tarball. For this task, generate_wafp_fingerprint.sh is to
be used.
+
+
+REQUIREMENTS (to be clarified)
+------------
+ruby >= 1.8
+sqlite3 >= 3
+sqlite3-ruby >= 1.2.4
+
+
+COMPILE AND INSTALL
+-------------------
+not needed.
+
+
+EXAMPLES
+--------
+./wafp.rb http://www.example.com/
+./wafp.rb -p 'wordpress' -v '2%' http://blog.example.com/
+./wafp.rb -f -t 32 -s phpmy-save01 -p 'phpmyadmin' -v '1.1.%'
https://user:pa...@www.example.com/phpmyadmin/
+./wafp.rb -d phpmy-save01 -p 'phpmyadmin' -v '1.1.%'
+
+try it, you will see it is as easy as 1,2,3 ;)
+
+AND - take a look at the HOWTO for more scenarios
+
+
+BUGS & FEATURES
+---------------
+drop me a line (or multiple) to
+richard[tod]sammet[ta]gmail[tod]com
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.9 (GNU/Linux)
+
+mQGiBEfp+9QRBACk0fpMU3+1ODvgeYONx+QEH9MiEoSbCK22md9hAGjeMmsTPboh
+b3kpBywg5k9j2beYzDL5FaTP1Fci6lbBkuTBMH5+H3liK9XTdDqGCGxr4R1pARlT
+hdqtJivcHVCc6FV++e74f9bcAeQNYs6qfqlCxTPn1zM8QT8FKZ1Ww6wRZwCg/XMf
+LRLpGmKV/x1L906OIJ9e/K8D/jOPY/xxc6u/3ytbH1kyYpUdBkgMskz2YvznBLFL
+qVMjT0sx17sNW7ia24oBWei9Sl2GeE2lpsgZ0qDWU4sMHIgbd3oiQimRkV/LWATi
+lZp0g8y43uXVYkAOabTWLTfVArS2sYMbuWikKSPryuy/DOndoUINEuTsZ9n0Jjyj
+FOPhA/9q5awfNkwL06885hSFvlVK/oUmviKqvI8XnB3Cg1eZzRfIAqEI+IV2IAGh
+BrJ2RHv6bX3ix8vYldX1LYnab5CeZSEtG9fPEy6Dshi/zfUYVG7QUkytOkNtLcFB
+eUZOepSRGddJnF2TvvcBnL8eTYcZjZkCJTyW1wnHRngjKQTXA7Q2UmljaGFyZCBT
+YW1tZXQgKGUtYXhlKSA8cmljaGFyZC5zYW1tZXRAZ29vZ2xlbWFpbC5jb20+iGYE
+ExECACYFAkfp+9QCGwMFCQeEzgAGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDy
+6IQSyfs793uoAKDtZjfch8XxdpfjqwtuUvK2kSo/ogCaAkraVudP0HCo2KeJnXuD
+UgM0//65BA0ER+n71BAQAMkeAvvmJLq3YmAf/xYOMUas3q+1HIWg0IDcZcMhkPQo
+YIP5BjqBk1M+icUr1cqsLKwUwRLyoS1uVusFbA27ikT39abp5fY94zkFS5cl5xVN
+byVCbKda6lpa4WHWa57eIz/4/BoWUcv34+pnh3dtP4GtPi0Z8HTLmCgOMV/QDY13
+Kf1E1vaS1Cuo1nXcqCg1eoiDYP03mQibsa0u0w4VRbxpjVDtmXJXLNCOHPOyV8QQ
+60usn0DHveDM9Mp623fSPZV7nOzUrT9sN0BIDNISN2kHLtSRsJ8ju3KOsHNTw60K
+qssj1LuMRJWHAm0RiS8tWHKL6YQegkIBOAFHAfk01BDcPOs3CwA1AQKyLrz7aO+w
+1wKhWjqGbcj+onPrg3vn0BECibNbFSnTTwUSeMWllsQY54d01NVYL/9qHbMObsYb
+cgu6bOF7BIbh0Gs8iNNxeh9bTFAIIH7Y9iiN3DgFLW1gHMlfRJTP2ZUyNkyHFdcS
+UKELmyuxMq+R2/zYxcvLmUqpcD/kW+RE4blSgDTvk0CIYs6yFiYsSuGDI4RpOvnH
+0qzrrTXwQyLwIX0sIkwX+tByoBLUH3YARlCDml7XRomzik0orhBqVR7qSHYlb1hc
+th2Wv1VkA++bFtf2Uh11zAnt030xt+T5iO/6Q3htTGDHcjag7IbjmWnO/vdR5Sab
+AAMGD/sFh7tozLXqgCrXiBHwvPT3Q/UEh+CdKCcSXKaepZCXl5ahN2b6kPi700Fm
+hcXNlRK4n3PoTSaCgcTx6WVKn8AqXR2W1mpStc2cxbCrODaR2fzuChVTMacsCzle
+nonI/tuFTiaVKXiNoVK2fX8WRJYmivmPpGz9Cp9YM3PylDGe4Z7BxVat83Yaa5DX
+6jbFWCI8UNJ6W7eIa2EIIIqcfk0pYgfQkfgGEDLjdjJdmHo3MIE7g57hq19/nyfu
+2y2+nPWtzXlKbNBkg/VeCNjNO2DDRnesp1Et68Wd2fnbLg0jqBGLjYXS8iPECPWi
+dwkHuptlY87xy7N6lCvU73iVr5rxBx49R11HEazNdbx1x0nF+Ht9Z37JDr+7qEWL
+ZFXKxWHgaUkHO23L0e+K5GSbzZQ5xcroYLeL9bfrsPd5kwAWjI4u1oyjBHWvxsiv
+kKgnmx61p/nZI0I1SAwc2DDEzBGIp20IAjox4GJkH2qX4SyffMxO+t3zb7lxZgFU
+PE7r0r5RXfL2NH5HpbUrLev4hBzlVLLA2cmMXj6gHJmw3fxdA3XQdH/zPIja0MDy
+t/sW7e5/owT8aiL6hboL7FiSgqLoPrC4Ru4FxfDnMG2LPqnxA4NFNjhaq3dIT8b1
+3O096iJD7cdqggjnER+tek9zty4mLZIZQmNaz/3JVLl6WhWEUohPBBgRAgAPBQJH
+6fvUAhsMBQkHhM4AAAoJEPLohBLJ+zv3tKoAoOR/0lcP/Q+7gSrpN8n64vJjpQu1
+AKCXdZdSbpFPLJny+S7CKQ13CaVL7A==
+=+atq
+-----END PGP PUBLIC KEY BLOCK-----
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/fprints_wafp.db Sat Jan 23 13:51:30 2010
File is too large to display a diff.
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/lib/fprints_wafp.schema Sat Jan 23 13:51:30
2010
@@ -0,0 +1,13 @@
+CREATE TABLE `tbl_fprint` (
+`id` INTEGER PRIMARY KEY NOT NULL ,
+`product_id` INTEGER NOT NULL ,
+`csum` VARCHAR(256) NOT NULL ,
+`path` MEDIUMTEXT NOT NULL
+);
+CREATE TABLE `tbl_product` (
+`id` INTEGER PRIMARY KEY NOT NULL ,
+`name` VARCHAR(256) NOT NULL ,
+`versionstring` VARCHAR(1024) NOT NULL ,
+`timestamp` VARCHAR(10) NOT NULL ,
+`info` MEDIUMTEXT NOT NULL
+);
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/lib/scan_wafp.schema Sat Jan 23 13:51:30
2010
@@ -0,0 +1,13 @@
+CREATE TABLE `tbl_results` (
+`id` INTEGER PRIMARY KEY NOT NULL ,
+`store_id` INTEGER NOT NULL ,
+`csum` VARCHAR(256) NOT NULL ,
+`path` MEDIUMTEXT NOT NULL ,
+`retcode` VARCHAR(4) NOT NULL
+);
+CREATE TABLE `tbl_store` (
+`id` INTEGER PRIMARY KEY NOT NULL ,
+`name` VARCHAR(256) NOT NULL ,
+`timestamp` VARCHAR(10) NOT NULL ,
+`info` MEDIUMTEXT NOT NULL
+);
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/lib/wafp_http.rb Sat Jan 23 13:51:30 2010
@@ -0,0 +1,2277 @@
+#
+# = net/http.rb
+#
+# Copyright (c) 1999-2006 Yukihiro Matsumoto
+# Copyright (c) 1999-2006 Minero Aoki
+# Copyright (c) 2001 GOTOU Yuuzou
+#
+# Written and maintained by Minero Aoki <aam...@loveruby.net>.
+# HTTPS support added by GOTOU Yuuzou <goto...@notwork.org>.
+#
+# This file is derived from "http-access.rb".
+#
+# Documented by Minero Aoki; converted to RDoc by William Webber.
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms of ruby itself ---
+# Ruby Distribution License or GNU General Public License.
+#
+# See Net::HTTP for an overview and examples.
+#
+# NOTE: You can find Japanese version of this document here:
+# http://www.ruby-lang.org/ja/man/?cmd=view;name=net%2Fhttp.rb
+#
+#--
+# $Id: http.rb 16878 2008-06-07 16:16:46Z shyouhei $
+#++
+
+require 'net/protocol'
+require 'uri'
+
+module Net #:nodoc:
+
+ # :stopdoc:
+ class HTTPBadResponse < StandardError; end
+ class HTTPHeaderSyntaxError < StandardError; end
+ # :startdoc:
+
+ # == What Is This Library?
+ #
+ # This library provides your program functions to access WWW
+ # documents via HTTP, Hyper Text Transfer Protocol version 1.1.
+ # For details of HTTP, refer [RFC2616]
+ # (http://www.ietf.org/rfc/rfc2616.txt).
+ #
+ # == Examples
+ #
+ # === Getting Document From WWW Server
+ #
+ # Example #1: Simple GET+print
+ #
+ # require 'net/http'
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
+ #
+ # Example #2: Simple GET+print by URL
+ #
+ # require 'net/http'
+ # require 'uri'
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
+ #
+ # Example #3: More generic GET+print
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # url = URI.parse('http://www.example.com/index.html')
+ # res = Net::HTTP.start(url.host, url.port) {|http|
+ # http.get('/index.html')
+ # }
+ # puts res.body
+ #
+ # Example #4: More generic GET+print
+ #
+ # require 'net/http'
+ #
+ # url = URI.parse('http://www.example.com/index.html')
+ # req = Net::HTTP::Get.new(url.path)
+ # res = Net::HTTP.start(url.host, url.port) {|http|
+ # http.request(req)
+ # }
+ # puts res.body
+ #
+ # === Posting Form Data
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # #1: Simple POST
+ # res =
Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
+ # {'q'=>'ruby', 'max'=>'50'})
+ # puts res.body
+ #
+ # #2: POST with basic authentication
+ # res =
Net::HTTP.post_form(URI.parse('http://jack:pa...@www.example.com/todo.cgi'),
+ #
{'from'=>'2005-01-01', 'to'=>'2005-03-31'})
+ # puts res.body
+ #
+ # #3: Detailed control
+ # url = URI.parse('http://www.example.com/todo.cgi')
+ # req = Net::HTTP::Post.new(url.path)
+ # req.basic_auth 'jack', 'pass'
+ # req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';')
+ # res = Net::HTTP.new(url.host, url.port).start {|http|
http.request(req) }
+ # case res
+ # when Net::HTTPSuccess, Net::HTTPRedirection
+ # # OK
+ # else
+ # res.error!
+ # end
+ #
+ # === Accessing via Proxy
+ #
+ # Net::HTTP.Proxy creates http proxy class. It has same
+ # methods of Net::HTTP but its instances always connect to
+ # proxy, instead of given host.
+ #
+ # require 'net/http'
+ #
+ # proxy_addr = 'your.proxy.host'
+ # proxy_port = 8080
+ # :
+ # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com')
{|http|
+ # # always connect to your.proxy.addr:8080
+ # :
+ # }
+ #
+ # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
+ # there's no need to change code if there's proxy or not.
+ #
+ # There are two additional parameters in Net::HTTP.Proxy which allow to
+ # specify proxy user name and password:
+ #
+ # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil,
proxy_pass = nil)
+ #
+ # You may use them to work with authorization-enabled proxies:
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # proxy_host = 'your.proxy.host'
+ # proxy_port = 8080
+ # uri = URI.parse(ENV['http_proxy'])
+ # proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
+ # Net::HTTP::Proxy(proxy_host, proxy_port,
+ # proxy_user, proxy_pass).start('www.example.com')
{|http|
+ # # always connect to your.proxy.addr:8080 using specified
username and password
+ # :
+ # }
+ #
+ # Note that net/http never rely on HTTP_PROXY environment variable.
+ # If you want to use proxy, set it explicitly.
+ #
+ # === Following Redirection
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # def fetch(uri_str, limit = 10)
+ # # You should choose better exception.
+ # raise ArgumentError, 'HTTP redirect too deep' if limit == 0
+ #
+ # response = Net::HTTP.get_response(URI.parse(uri_str))
+ # case response
+ # when Net::HTTPSuccess then response
+ # when Net::HTTPRedirection then fetch(response['location'], limit
- 1)
+ # else
+ # response.error!
+ # end
+ # end
+ #
+ # print fetch('http://www.ruby-lang.org')
+ #
+ # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
+ # All HTTPResponse objects belong to its own response class which
+ # indicate HTTP result status. For details of response classes,
+ # see section "HTTP Response Classes".
+ #
+ # === Basic Authentication
+ #
+ # require 'net/http'
+ #
+ # Net::HTTP.start('www.example.com') {|http|
+ # req = Net::HTTP::Get.new('/secret-page.html')
+ # req.basic_auth 'account', 'password'
+ # response = http.request(req)
+ # print response.body
+ # }
+ #
+ # === HTTP Request Classes
+ #
+ # Here is HTTP request class hierarchy.
+ #
+ # Net::HTTPRequest
+ # Net::HTTP::Get
+ # Net::HTTP::Head
+ # Net::HTTP::Post
+ # Net::HTTP::Put
+ # Net::HTTP::Proppatch
+ # Net::HTTP::Lock
+ # Net::HTTP::Unlock
+ # Net::HTTP::Options
+ # Net::HTTP::Propfind
+ # Net::HTTP::Delete
+ # Net::HTTP::Move
+ # Net::HTTP::Copy
+ # Net::HTTP::Mkcol
+ # Net::HTTP::Trace
+ #
+ # === HTTP Response Classes
+ #
+ # Here is HTTP response class hierarchy.
+ # All classes are defined in Net module.
+ #
+ # HTTPResponse
+ # HTTPUnknownResponse
+ # HTTPInformation # 1xx
+ # HTTPContinue # 100
+ # HTTPSwitchProtocl # 101
+ # HTTPSuccess # 2xx
+ # HTTPOK # 200
+ # HTTPCreated # 201
+ # HTTPAccepted # 202
+ # HTTPNonAuthoritativeInformation # 203
+ # HTTPNoContent # 204
+ # HTTPResetContent # 205
+ # HTTPPartialContent # 206
+ # HTTPRedirection # 3xx
+ # HTTPMultipleChoice # 300
+ # HTTPMovedPermanently # 301
+ # HTTPFound # 302
+ # HTTPSeeOther # 303
+ # HTTPNotModified # 304
+ # HTTPUseProxy # 305
+ # HTTPTemporaryRedirect # 307
+ # HTTPClientError # 4xx
+ # HTTPBadRequest # 400
+ # HTTPUnauthorized # 401
+ # HTTPPaymentRequired # 402
+ # HTTPForbidden # 403
+ # HTTPNotFound # 404
+ # HTTPMethodNotAllowed # 405
+ # HTTPNotAcceptable # 406
+ # HTTPProxyAuthenticationRequired # 407
+ # HTTPRequestTimeOut # 408
+ # HTTPConflict # 409
+ # HTTPGone # 410
+ # HTTPLengthRequired # 411
+ # HTTPPreconditionFailed # 412
+ # HTTPRequestEntityTooLarge # 413
+ # HTTPRequestURITooLong # 414
+ # HTTPUnsupportedMediaType # 415
+ # HTTPRequestedRangeNotSatisfiable # 416
+ # HTTPExpectationFailed # 417
+ # HTTPServerError # 5xx
+ # HTTPInternalServerError # 500
+ # HTTPNotImplemented # 501
+ # HTTPBadGateway # 502
+ # HTTPServiceUnavailable # 503
+ # HTTPGatewayTimeOut # 504
+ # HTTPVersionNotSupported # 505
+ #
+ # == Switching Net::HTTP versions
+ #
+ # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
+ # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
+ # allows you to use 1.2 features again.
+ #
+ # # example
+ # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
+ #
+ # Net::HTTP.version_1_1
+ # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
+ #
+ # Net::HTTP.version_1_2
+ # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
+ #
+ # This function is NOT thread-safe.
+ #
+ class HTTP < Protocol
+
+ # :stopdoc:
+ Revision = %q$Revision: 16878 $.split[1]
+ HTTPVersion = '1.1'
+ @newimpl = true
+ # :startdoc:
+
+ # Turns on net/http 1.2 (ruby 1.8) features.
+ # Defaults to ON in ruby 1.8.
+ #
+ # I strongly recommend to call this method always.
+ #
+ # require 'net/http'
+ # Net::HTTP.version_1_2
+ #
+ def HTTP.version_1_2
+ @newimpl = true
+ end
+
+ # Turns on net/http 1.1 (ruby 1.6) features.
+ # Defaults to OFF in ruby 1.8.
+ def HTTP.version_1_1
+ @newimpl = false
+ end
+
+ # true if net/http is in version 1.2 mode.
+ # Defaults to true.
+ def HTTP.version_1_2?
+ @newimpl
+ end
+
+ # true if net/http is in version 1.1 compatible mode.
+ # Defaults to true.
+ def HTTP.version_1_1?
+ not @newimpl
+ end
+
+ class << HTTP
+ alias is_version_1_1? version_1_1? #:nodoc:
+ alias is_version_1_2? version_1_2? #:nodoc:
+ end
+
+ #
+ # short cut methods
+ #
+
+ #
+ # Get body from target and output it to +$stdout+. The
+ # target can either be specified as (+uri+), or as
+ # (+host+, +path+, +port+ = 80); so:
+ #
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
+ #
+ # or:
+ #
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
+ #
+ def HTTP.get_print(uri_or_host, path = nil, port = nil)
+ get_response(uri_or_host, path, port) {|res|
+ res.read_body do |chunk|
+ $stdout.print chunk
+ end
+ }
+ nil
+ end
+
+ # Send a GET request to the target and return the response
+ # as a string. The target can either be specified as
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
+ #
+ # print
Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
+ #
+ # or:
+ #
+ # print Net::HTTP.get('www.example.com', '/index.html')
+ #
+ def HTTP.get(uri_or_host, path = nil, port = nil)
+ get_response(uri_or_host, path, port).body
+ end
+
+ # Send a GET request to the target and return the response
+ # as a Net::HTTPResponse object. The target can either be specified as
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
+ #
+ # res =
Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
+ # print res.body
+ #
+ # or:
+ #
+ # res = Net::HTTP.get_response('www.example.com', '/index.html')
+ # print res.body
+ #
+ def HTTP.get_response(uri_or_host, path = nil, port = nil, initheader
= nil, &block)
+ if path
+ host = uri_or_host
+ new(host, port || HTTP.default_port).start {|http|
+ return http.request_get(path, initheader, &block)
+ }
+ else
+ uri = uri_or_host
+ new(uri.host, uri.port).start {|http|
+ return http.request_get(uri.request_uri, initheader, &block)
+ }
+ end
+ end
+
+ # Posts HTML form data to the +URL+.
+ # Form data must be represented as a Hash of String to String, e.g:
+ #
+ # { "cmd" => "search", "q" => "ruby", "max" => "50" }
+ #
+ # This method also does Basic Authentication iff +URL+.user exists.
+ #
+ # Example:
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # HTTP.post_form URI.parse('http://www.example.com/search.cgi'),
+ # { "q" => "ruby", "max" => "50" }
+ #
+ def HTTP.post_form(url, params)
+ req = Post.new(url.path)
+ req.form_data = params
+ req.basic_auth url.user, url.password if url.user
+ new(url.host, url.port).start {|http|
+ http.request(req)
+ }
+ end
+
+ #
+ # HTTP session management
+ #
+
+ # The default port to use for HTTP requests; defaults to 80.
+ def HTTP.default_port
+ http_default_port()
+ end
+
+ # The default port to use for HTTP requests; defaults to 80.
+ def HTTP.http_default_port
+ 80
+ end
+
+ # The default port to use for HTTPS requests; defaults to 443.
+ def HTTP.https_default_port
+ 443
+ end
+
+ def HTTP.socket_type #:nodoc: obsolete
+ BufferedIO
+ end
+
+ # creates a new Net::HTTP object and opens its TCP connection and
+ # HTTP session. If the optional block is given, the newly
+ # created Net::HTTP object is passed to it and closed when the
+ # block finishes. In this case, the return value of this method
+ # is the return value of the block. If no block is given, the
+ # return value of this method is the newly created Net::HTTP object
+ # itself, and the caller is responsible for closing it upon completion.
+ def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user
= nil, p_pass = nil, &block) # :yield: +http+
+ new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
+ end
+
+ class << HTTP
+ alias newobj new
+ end
+
+ # Creates a new Net::HTTP object.
+ # If +proxy_addr+ is given, creates an Net::HTTP object with proxy
support.
+ # This method does not open the TCP connection.
+ def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user =
nil, p_pass = nil)
+ h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
+ h.instance_eval {
+ @newimpl = ::Net::HTTP.version_1_2?
+ }
+ h
+ end
+
+ # Creates a new Net::HTTP object for the specified +address+.
+ # This method does not open the TCP connection.
+ def initialize(address, port = nil)
+ @address = address
+ @port = (port || HTTP.default_port)
+ @curr_http_version = HTTPVersion
+ @seems_1_0_server = false
+ @close_on_empty_response = false
+ @socket = nil
+ @started = false
+ @open_timeout = nil
+ @read_timeout = 60
+ @debug_output = nil
+ @use_ssl = false
+ @ssl_context = nil
+ end
+
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
+ end
+
+ # *WARNING* This method causes serious security hole.
+ # Never use this method in production code.
+ #
+ # Set an output stream for debugging.
+ #
+ # http = Net::HTTP.new
+ # http.set_debug_output $stderr
+ # http.start { .... }
+ #
+ def set_debug_output(output)
+ warn 'Net::HTTP#set_debug_output called after HTTP started' if
started?
+ @debug_output = output
+ end
+
+ # The host name to connect to.
+ attr_reader :address
+
+ # The port number to connect to.
+ attr_reader :port
+
+ # Seconds to wait until connection is opened.
+ # If the HTTP object cannot open a connection in this many seconds,
+ # it raises a TimeoutError exception.
+ attr_accessor :open_timeout
+
+ # Seconds to wait until reading one block (by one read(2) call).
+ # If the HTTP object cannot open a connection in this many seconds,
+ # it raises a TimeoutError exception.
+ attr_reader :read_timeout
+
+ # Setter for the read_timeout attribute.
+ def read_timeout=(sec)
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ # returns true if the HTTP session is started.
+ def started?
+ @started
+ end
+
+ alias active? started? #:nodoc: obsolete
+
+ attr_accessor :close_on_empty_response
+
+ # returns true if use SSL/TLS with HTTP.
+ def use_ssl?
+ false # redefined in net/https
+ end
+
+ # Opens TCP connection and HTTP session.
+ #
+ # When this method is called with block, gives a HTTP object
+ # to the block and closes the TCP connection / HTTP session
+ # after the block executed.
+ #
+ # When called with a block, returns the return value of the
+ # block; otherwise, returns self.
+ #
+ def start # :yield: http
+ raise IOError, 'HTTP session already opened' if @started
+ if block_given?
+ begin
+ do_start
+ return yield(self)
+ ensure
+ do_finish
+ end
+ end
+ do_start
+ self
+ end
+
+ def do_start
+ connect
+ @started = true
+ end
+ private :do_start
+
+ def connect
+ D "opening connection to #{conn_address()}..."
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(),
conn_port()) }
+ D "opened"
+ if use_ssl?
+ unless @ssl_context.verify_mode
+ warn "warning: peer certificate won't be verified in this SSL
session"
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
+ s.sync_close = true
+ end
+ @socket = BufferedIO.new(s)
+ @socket.read_timeout = @read_timeout
+ @socket.debug_output = @debug_output
+ if use_ssl?
+ if proxy?
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
+ @address, @port, HTTPVersion)
+ @socket.writeline "Host: #{@address}:#{@port}"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
+ credential.delete!("\r\n")
+ @socket.writeline "Proxy-Authorization: Basic #{credential}"
+ end
+ @socket.writeline ''
+ HTTPResponse.read_new(@socket).value
+ end
+ s.connect
+ if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
+ s.post_connection_check(@address)
+ end
+ end
+ on_connect
+ end
+ private :connect
+
+ def on_connect
+ end
+ private :on_connect
+
+ # Finishes HTTP session and closes TCP connection.
+ # Raises IOError if not started.
+ def finish
+ raise IOError, 'HTTP session not yet started' unless started?
+ do_finish
+ end
+
+ def do_finish
+ @started = false
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ end
+ private :do_finish
+
+ #
+ # proxy
+ #
+
+ public
+
+ # no proxy
+ @is_proxy_class = false
+ @proxy_addr = nil
+ @proxy_port = nil
+ @proxy_user = nil
+ @proxy_pass = nil
+
+ # Creates an HTTP proxy class.
+ # Arguments are address/port of proxy host and username/password
+ # if authorization on proxy server is required.
+ # You can replace the HTTP class with created proxy class.
+ #
+ # If ADDRESS is nil, this method returns self (Net::HTTP).
+ #
+ # # Example
+ # proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
+ # :
+ # proxy_class.start('www.ruby-lang.org') {|http|
+ # # connecting proxy.foo.org:8080
+ # :
+ # }
+ #
+ def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
+ return self unless p_addr
+ delta = ProxyDelta
+ proxyclass = Class.new(self)
+ proxyclass.module_eval {
+ include delta
+ # with proxy
+ @is_proxy_class = true
+ @proxy_address = p_addr
+ @proxy_port = p_port || default_port()
+ @proxy_user = p_user
+ @proxy_pass = p_pass
+ }
+ proxyclass
+ end
+
+ class << HTTP
+ # returns true if self is a class which was created by HTTP::Proxy.
+ def proxy_class?
+ @is_proxy_class
+ end
+
+ attr_reader :proxy_address
+ attr_reader :proxy_port
+ attr_reader :proxy_user
+ attr_reader :proxy_pass
+ end
+
+ # True if self is a HTTP proxy class.
+ def proxy?
+ self.class.proxy_class?
+ end
+
+ # Address of proxy host. If self does not use a proxy, nil.
+ def proxy_address
+ self.class.proxy_address
+ end
+
+ # Port number of proxy host. If self does not use a proxy, nil.
+ def proxy_port
+ self.class.proxy_port
+ end
+
+ # User name for accessing proxy. If self does not use a proxy, nil.
+ def proxy_user
+ self.class.proxy_user
+ end
+
+ # User password for accessing proxy. If self does not use a proxy, nil.
+ def proxy_pass
+ self.class.proxy_pass
+ end
+
+ alias proxyaddr proxy_address #:nodoc: obsolete
+ alias proxyport proxy_port #:nodoc: obsolete
+
+ private
+
+ # without proxy
+
+ def conn_address
+ address()
+ end
+
+ def conn_port
+ port()
+ end
+
+ def edit_path(path)
+ path
+ end
+
+ module ProxyDelta #:nodoc: internal use only
+ private
+
+ def conn_address
+ proxy_address()
+ end
+
+ def conn_port
+ proxy_port()
+ end
+
+ def edit_path(path)
+ use_ssl? ? path : "http://#{addr_port()}#{path}"
+ end
+ end
+
+ #
+ # HTTP operations
+ #
+
+ public
+
+ # Gets data from +path+ on the connected-to host.
+ # +header+ must be a Hash like { 'Accept' => '*/*', ... }.
+ #
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects,
+ # a Net::HTTPResponse object and the entity body string.
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
+ # object.
+ #
+ # If called with a block, yields each fragment of the
+ # entity body in turn as a string as it is read from
+ # the socket. Note that in this case, the returned response
+ # object will *not* contain a (meaningful) body.
+ #
+ # +dest+ argument is obsolete.
+ # It still works but you must not use it.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). In this case you can get a HTTPResponse object
+ # by "anException.response".
+ #
+ # In version 1.2, this method never raises exception.
+ #
+ # # version 1.1 (bundled with Ruby 1.6)
+ # response, body = http.get('/index.html')
+ #
+ # # version 1.2 (bundled with Ruby 1.8 or later)
+ # response = http.get('/index.html')
+ #
+ # # using block
+ # File.open('result.txt', 'w') {|f|
+ # http.get('/~foo/') do |str|
+ # f.write str
+ # end
+ # }
+ #
+ def get(path, initheader = nil, dest = nil, &block) # :yield:
+body_segment+
+ res = nil
+ request(Get.new(path, initheader)) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ unless @newimpl
+ res.value
+ return res, res.body
+ end
+
+ res
+ end
+
+ # Gets only the header from +path+ on the connected-to host.
+ # +header+ is a Hash like { 'Accept' => '*/*', ... }.
+ #
+ # This method returns a Net::HTTPResponse object.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). On the case you can get a HTTPResponse object
+ # by "anException.response".
+ # In version 1.2, this method never raises an exception.
+ #
+ # response = nil
+ # Net::HTTP.start('some.www.server', 80) {|http|
+ # response = http.head('/index.html')
+ # }
+ # p response['content-type']
+ #
+ def head(path, initheader = nil)
+ res = request(Head.new(path, initheader))
+ res.value unless @newimpl
+ res
+ end
+
+ # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
+ # like { 'Accept' => '*/*', ... }.
+ #
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
+ # Net::HTTPResponse object and an entity body string.
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
object.
+ #
+ # If called with a block, yields each fragment of the
+ # entity body in turn as a string as it are read from
+ # the socket. Note that in this case, the returned response
+ # object will *not* contain a (meaningful) body.
+ #
+ # +dest+ argument is obsolete.
+ # It still works but you must not use it.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). In this case you can get an HTTPResponse object
+ # by "anException.response".
+ # In version 1.2, this method never raises exception.
+ #
+ # # version 1.1
+ # response, body = http.post('/cgi-bin/search.rb', 'query=foo')
+ #
+ # # version 1.2
+ # response = http.post('/cgi-bin/search.rb', 'query=foo')
+ #
+ # # using block
+ # File.open('result.txt', 'w') {|f|
+ # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
+ # f.write str
+ # end
+ # }
+ #
+ # You should set Content-Type: header field for POST.
+ # If no Content-Type: field given, this method uses
+ # "application/x-www-form-urlencoded" by default.
+ #
+ def post(path, data, initheader = nil, dest = nil, &block) # :yield:
+body_segment+
+ res = nil
+ request(Post.new(path, initheader), data) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ unless @newimpl
+ res.value
+ return res, res.body
+ end
+ res
+ end
+
+ def put(path, data, initheader = nil) #:nodoc:
+ res = request(Put.new(path, initheader), data)
+ res.value unless @newimpl
+ res
+ end
+
+ # Sends a PROPPATCH request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def proppatch(path, body, initheader = nil)
+ request(Proppatch.new(path, initheader), body)
+ end
+
+ # Sends a LOCK request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def lock(path, body, initheader = nil)
+ request(Lock.new(path, initheader), body)
+ end
+
+ # Sends a UNLOCK request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def unlock(path, body, initheader = nil)
+ request(Unlock.new(path, initheader), body)
+ end
+
+ # Sends a OPTIONS request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def options(path, initheader = nil)
+ request(Options.new(path, initheader))
+ end
+
+ # Sends a PROPFIND request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def propfind(path, body = nil, initheader = {'Depth' => '0'})
+ request(Propfind.new(path, initheader), body)
+ end
+
+ # Sends a DELETE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def delete(path, initheader = {'Depth' => 'Infinity'})
+ request(Delete.new(path, initheader))
+ end
+
+ # Sends a MOVE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def move(path, initheader = nil)
+ request(Move.new(path, initheader))
+ end
+
+ # Sends a COPY request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def copy(path, initheader = nil)
+ request(Copy.new(path, initheader))
+ end
+
+ # Sends a MKCOL request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def mkcol(path, body = nil, initheader = nil)
+ request(Mkcol.new(path, initheader), body)
+ end
+
+ # Sends a TRACE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def trace(path, initheader = nil)
+ request(Trace.new(path, initheader))
+ end
+
+ # Sends a GET request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.request_get('/index.html')
+ # # The entity body is already read here.
+ # p response['content-type']
+ # puts response.body
+ #
+ # # using block
+ # http.request_get('/index.html') {|response|
+ # p response['content-type']
+ # response.read_body do |str| # read body now
+ # print str
+ # end
+ # }
+ #
+ def request_get(path, initheader, &block) # :yield: +response+
+ request(Get.new(path, initheader), &block)
+ end
+
+ # Sends a HEAD request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.request_head('/index.html')
+ # p response['content-type']
+ #
+ def request_head(path, initheader = nil, &block)
+ request(Head.new(path, initheader), &block)
+ end
+
+ # Sends a POST request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # # example
+ # response =
http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
+ # p response.status
+ # puts response.body # body is already read
+ #
+ # # using block
+ # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|
response|
+ # p response.status
+ # p response['content-type']
+ # response.read_body do |str| # read body now
+ # print str
+ # end
+ # }
+ #
+ def request_post(path, data, initheader = nil, &block) # :yield:
+response+
+ request Post.new(path, initheader), data, &block
+ end
+
+ def request_put(path, data, initheader = nil, &block) #:nodoc:
+ request Put.new(path, initheader), data, &block
+ end
+
***The diff for this file has been truncated for email.***
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/lib/wafp_https.rb Sat Jan 23 13:51:30 2010
@@ -0,0 +1,173 @@
+=begin
+
+= $RCSfile$ -- SSL/TLS enhancement for Net::HTTP.
+
+== Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2001 GOTOU Yuuzou <goto...@notwork.org>
+ All rights reserved.
+
+== Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+== Requirements
+ This program requires Net 1.2.0 or higher version.
+ You can get it from RAA or Ruby's CVS repository.
+
+== Version
+ $Id: https.rb 16857 2008-06-06 08:05:24Z knu $
+
+ 2001-11-06: Contiributed to Ruby/OpenSSL project.
+ 2004-03-06: Some code is merged in to net/http.
+
+== Example
+
+Here is a simple HTTP client:
+
+ require 'net/http'
+ require 'uri'
+
+ uri = URI.parse(ARGV[0] || 'http://localhost/')
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.start {
+ http.request_get(uri.path) {|res|
+ print res.body
+ }
+ }
+
+It can be replaced by the following code:
+
+ require 'net/https'
+ require 'uri'
+
+ uri = URI.parse(ARGV[0] || 'https://localhost/')
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true if uri.scheme == "https" # enable SSL/TLS
+ http.start {
+ http.request_get(uri.path) {|res|
+ print res.body
+ }
+ }
+
+== class Net::HTTP
+
+=== Instance Methods
+
+: use_ssl?
+ returns true if use SSL/TLS with HTTP.
+
+: use_ssl=((|true_or_false|))
+ sets use_ssl.
+
+: peer_cert
+ return the X.509 certificates the server presented.
+
+: key, key=((|key|))
+ Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ (This method is appeared in Michal Rokos's OpenSSL extension.)
+
+: cert, cert=((|cert|))
+ Sets an OpenSSL::X509::Certificate object as client certificate
+ (This method is appeared in Michal Rokos's OpenSSL extension).
+
+: ca_file, ca_file=((|path|))
+ Sets path of a CA certification file in PEM format.
+ The file can contrain several CA certificats.
+
+: ca_path, ca_path=((|path|))
+ Sets path of a CA certification directory containing certifications
+ in PEM format.
+
+: verify_mode, verify_mode=((|mode|))
+ Sets the flags for server the certification verification at
+ begining of SSL/TLS session.
+ OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable.
+
+: verify_callback, verify_callback=((|proc|))
+ Sets the verify callback for the server certification verification.
+
+: verify_depth, verify_depth=((|num|))
+ Sets the maximum depth for the certificate chain verification.
+
+: cert_store, cert_store=((|store|))
+ Sets the X509::Store to verify peer certificate.
+
+: ssl_timeout, ssl_timeout=((|sec|))
+ Sets the SSL timeout seconds.
+
+=end
+
+require "lib/wafp_http.rb"
+require 'openssl'
+
+module Net
+
+ class HTTP
+ remove_method :use_ssl?
+ def use_ssl?
+ @use_ssl
+ end
+
+ # For backward compatibility.
+ alias use_ssl use_ssl?
+
+ # Turn on/off SSL.
+ # This flag must be set before starting session.
+ # If you change use_ssl value after session started,
+ # a Net::HTTP object raises IOError.
+ def use_ssl=(flag)
+ flag = (flag ? true : false)
+ raise IOError, "use_ssl value changed, but session already started" \
+ if started? and @use_ssl != flag
+ if flag and not @ssl_context
+ @ssl_context = OpenSSL::SSL::SSLContext.new
+ end
+ @use_ssl = flag
+ end
+
+ def self.ssl_context_accessor(name)
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def #{name}
+ return nil unless @ssl_context
+ @ssl_context.#{name}
+ end
+
+ def #{name}=(val)
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new
+ @ssl_context.#{name} = val
+ end
+ End
+ end
+
+ ssl_context_accessor :key
+ ssl_context_accessor :cert
+ ssl_context_accessor :ca_file
+ ssl_context_accessor :ca_path
+ ssl_context_accessor :verify_mode
+ ssl_context_accessor :verify_callback
+ ssl_context_accessor :verify_depth
+ ssl_context_accessor :cert_store
+
+ def ssl_timeout
+ return nil unless @ssl_context
+ @ssl_context.timeout
+ end
+
+ def ssl_timeout=(sec)
+ raise ArgumentError, 'Net::HTTP#ssl_timeout= called but
use_ssl=false' \
+ unless use_ssl?
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new
+ @ssl_context.timeout = sec
+ end
+
+ # For backward compatibility
+ alias timeout= ssl_timeout=
+
+ def peer_cert
+ return nil if not use_ssl? or not @socket
+ @socket.io.peer_cert
+ end
+ end
+
+end
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/lib/wafp_pidify.rb Sat Jan 23 13:51:30 2010
@@ -0,0 +1,223 @@
+# = Pidify
+#
+# == Synopsis
+#
+# A Ruby module to simplify storing and deleting the PID of a running
program.
+# It also provides the ability to kill a running program whose PID it has
+# already saved. This allows a program to check if there is currently
another
+# running instance of itself, and give it the ability to kill that instance
+# based on PID.
+#
+# Note that this does no special process checking and relies solely on the
PID
+# files it creates and maintains.
+#
+# See Pidify for more information and examples.
+#
+# == Website
+#
+# Documentation:
+# http://pidify.rubyforge.org/
+#
+# Rubyforge page and Download:
+# http://rubyforge.org/projects/pidify/
+#
+# == Requirements
+#
+# - Ruby 1.8
+# - Pathname module
+#
+# == Author
+# Payton Swick, 2005
+#
+# == License
+# Creative Commons Attribution
+# http://creativecommons.org/licenses/by/2.0/
+#
+
+# Use the module methods in Pidify to save/delete the PID of a running
script,
+# or kill a running script using a saved PID.
+#
+# Example:
+# require 'pidify'
+# Pidify.running? # => false
+# Pidify.start
+# Pidify.running? # => true
+# puts "I am running with PID #{Pidify.pid}!"
+# Pidify.stop
+# Pidify.running? # => false
+#
+# A more useful example:
+# require 'pidify'
+# Signal.trap('INT') { Pidify.stop; exit }
+# module Doer
+# def self.start
+# puts "starting"
+# Pidify.start_as_daemon
+# loop do
+# puts "hello world"
+# sleep 1
+# end
+# end
+# end
+# if ARGV.include? 'stop'
+# Pidify.stop
+# puts "Daemon stopped."
+# else
+# puts "Daemon starting."
+# Doer.start
+# end
+#
+module Pidify
+ require 'pathname'
+ @pid_directory = Pathname.new("/tmp")
+ @file_name = $0
+
+ class << self
+ # Returns the Pathname of the PID storage directory (defaults to
/var/run).
+ def pid_directory
+ @pid_directory
+ end
+
+ # Sets the PID storage directory (defaults to /var/run). Be VERY
CAREFUL
+ # using this, as delete_pid will try to delete whatever file it thinks
is
+ # the pid_file for this script in the pid_directory. It's probably a
good
+ # idea not to change this at all.
+ def pid_directory=(dir)
+ @pid_directory = Pathname.new(dir) unless dir.kind_of? Pathname
+ @pid_directory = dir if dir.kind_of? Pathname
+ end
+
+ # Returns the PID filename as a Pathname.
+ def pid_file
+ @pid_directory + (Pathname.new(@file_name).basename.to_s+'.pid')
+ end
+
+ # Returns true if the pid_file exists for this script.
+ def pid_exists?
+ return FileTest.exists?(pid_file)
+ end
+
+ # Returns true if the process using pid is running.
+ def running?
+ return false unless pid_exists?
+ begin
+ Process::kill 0, pid
+ true
+ rescue Errno::ESRCH
+ false
+ end
+ end
+
+ # Returns the PID stored in the pid_file (not necessarily the PID of
this
+ # script). Returns nil if no PID exists or if there is a problem with
the
+ # read.
+ def pid
+ return nil unless pid_exists?
+ dpid = nil
+ begin
+ File.open(pid_file, File::RDONLY) { |file| dpid = file.gets.chomp
if file.flock(File::LOCK_SH|File::LOCK_NB); file.flock(File::LOCK_UN) }
+ rescue
+ return nil
+ end
+ return dpid.to_i if dpid && dpid.to_i > 0
+ nil
+ end
+
+ # Saves the PID of this script into the pid_file. Automatically
called by
+ # start. Returns nil if the pid file already exists. Returns true if
+ # successful, false if there was a write problem.
+ def save_pid
+ return nil if pid_exists?
+ begin
+ File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) { |file|
file.puts $$ if file.flock(File::LOCK_EX); file.flock(File::LOCK_UN) }
+ true
+ rescue
+ false
+ end
+ end
+
+ # Deletes the PID file. Calling stop calls this automatically, but
will
+ # also try to send a kill signal to the running process, if it is
different
+ # from this one. BEWARE that this tries to delete whatever file is
+ # returned by pid_file and does no error checking on it! Returns true
if
+ # the delete was successful, false if there was an error, and nil if
the
+ # pid file doesn't exist.
+ def delete_pid
+ return nil unless pid_exists?
+ begin
+ # FIXME: lock first?
+ File.delete(pid_file)
+ true
+ rescue
+ false
+ end
+ end
+
+ # Saves the PID of this script into the pid_file by calling save_pid.
+ # Raises an exception if pid_exists? returns false. Returns true if
+ # successful.
+ def start
+ raise "Failed to start: already running (PID file exists)." if
pid_exists?
+ return true if save_pid
+ end
+
+ # Deletes the saved PID file and, if the PID belongs to a process
different
+ # from this script, sends kill signals to the saved PID using pid_end.
+ # Returns true if the process was killed or false otherwise.
+ def stop(signals=%w(SIGTERM SIGQUIT SIGKILL), secs_between_signal=4)
+ return false unless pid_exists?
+ unless running?
+ delete_pid
+ return true
+ end
+ pid = self.pid
+ killed = true
+ killed = pid_end(signals, secs_between_signal) if pid != $$
+ delete_pid if killed == true
+ killed
+ end
+
+ # Sends each kill signal to the saved PID, pausing for
secs_between_signal
+ # after each to check if it the process remains running. Stops when
the
+ # process has ended or when all signals have been tried. Returns true
if
+ # the process was killed or false otherwise.
+ def pid_end(signals=%w(SIGTERM SIGQUIT SIGKILL), secs_between_signal=4)
+ pid = self.pid
+ signals = [ signals ].flatten.map{|sig| sig.to_s}
+ existed = false
+ signals.each do |sig|
+ begin
+ Process.kill(sig, pid)
+ existed = true
+ rescue Errno::ESRCH
+ return (existed ? true : nil)
+ end
+ return true unless running?
+ sleep secs_between_signal
+ return true unless running?
+ end
+ not running?
+ end
+
+ # Like Pidify.start, but first calls Pidify.daemonize. Will fail and
raise
+ # an exception if Pidify.running? returns true.
+ def start_as_daemon
+ raise "Failed to start: already running." if running?
+ daemonize
+ start
+ end
+
+ # Daemonizes this process. Does not automatically use a PID file. If
you
+ # want to use a PID file, you must call Pidify.start after the call to
+ # daemonize or use Pidify.start_as_daemon.
+ def daemonize
+ fork and exit
+ Process.setsid
+ Dir.chdir '/'
+ File.umask 0000
+ STDIN.reopen "/dev/null"
+ STDOUT.reopen "/dev/null", "a"
+ STDERR.reopen STDOUT
+ end
+ end
+end
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/scan_wafp.db Sat Jan 23 13:51:30 2010
Binary file, no diff available.
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/utils/README.generate_wafp_fingerprint Sat
Jan 23 13:51:30 2010
@@ -0,0 +1,47 @@
+ WAFP
+ (c) Richard Sammet (e-axe)
+ http://mytty.org/wafp/
+
+
+INTRODUCTION
+------------
+It is strongly recommended to read the following lines before you use WAFP
to generate your own Web Application Finger Prints, because
+the execution of the script in a wrong way could destroy your existing
Finger Print Database.
+
+
+USAGE
+-----
+The script will be executed via:
+
+./generate_wafp_fingerprint.sh [OPTIONS]
+
+
+The following list contains the available Options:
+--
+
+ APPLICATION_PATH should be the path to the Web Application files - like
the content of the htdocs folder of a wordpress installation.
+ The folder containing the Web Application files must be
named like this:
+
APPLICATIONNAME-MAJORVERSION.VERSION.SUB.ASO-RC-ALPHA-BETA
+ FINGERPRINT_FILE is the path to a file containing exported Finger Prints.
+ EXPORT this means the generated Finger Print data will not be
stored within the DB but printed to stdout
+ in a format which can be imported with IMPORT:
+ IMPORT can be used to import some EXPORTED fingerprints.
+
+
+EXAMPLE USAGE
+-------------
+ ./generate_wafp_fingerprint.sh /tmp/wordpress-2.7.1
+ where /tmp/wordpress-2.7.1 holds the extracted
wordpress tarball.
+ This will generate the Finger Prints for the
provided wordpress version and stores it inside
+ the SQL database.
+ ./generate_wafp_fingerprint.sh /tmp/wordpress-2.7.1
EXPORT > wordpress-2.7.1.fp
+ this will do exactly the same like the first
example but it will not store the Finger Print data inside the DB.
+ The Finger Print data is written to STDOUT where it
gets redirected to a file called wordpress-2.7.1.fp.
+ ./generate_wafp_fingerprint.sh wordpress-2.7.1.fp IMPORT
+ this will import the Finger Print data contained in
wordpress-2.7.1.fp into the sql database.
+
+
+Now that you know enough about generating WAFP finger prints you should do
the following:
+touch i_read_README.generate_wafp_fingerprint
+
+After you created the file "i_read_README.generate_wafp_fingerprint" you
will be able to execute generate_wafp_fingerprint.sh.
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/utils/SUBMIT_YOUR_FPS Sat Jan 23 13:51:30
2010
@@ -0,0 +1,10 @@
+I kindly ask you to submit valuable fingerprints you generated to:
+richard...@gmail.com
+
+After I checked those finger Prints I will add them to the database
+shipped with WAFP and to the online update system.
+
+please use a subject like: [wafp-fp] wordpress-2.9.1
+otherwise I will not be able to locate your Finger Prints in my mailbox ;)
+
+Please also consider to use bzip2 or gzip :)
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/utils/extract_from_db.sh Sat Jan 23
13:51:30 2010
@@ -0,0 +1,35 @@
+#!/bin/bash
+###
+# this script extracts the products/fingerprints from the db
+# to a bunch of files - each file containing the fps for one
+# version of a product.
+###
+
+if [ "X$1" != "X" ] ; then
+ filter="WHERE name LIKE \"$1\""
+else
+ filter=""
+fi
+
+wafp_fp_db="../fprints_wafp.db"
+
+rm -rf /tmp/wafp_computed_fps/
+mkdir /tmp/wafp_computed_fps/
+
+sqlite3 $wafp_fp_db "SELECT DISTINCT * FROM tbl_product $filter" |
+while read i ; do
+ product_id=`echo $i | awk -F'|' '{ print $1 }'`
+ product=`echo $i | awk -F'|' '{ print $2 }'`
+ versionstring=`echo $i | awk -F'|' '{ print $3 }'`
+ timestamp=`echo $i | awk -F'|' '{ print $4 }'`
+ info=`echo $i | awk -F'|' '{ print $5 }'`
+ echo "-- NEW_PRODUCT - START" >
/tmp/wafp_computed_fps/${timestamp}___${product}___${versionstring}.fp
+ echo "INSERT INTO tbl_product VALUES(NULL, \"$product\",
\"$versionstring\", \"$timestamp\", \"$info\")" >>
/tmp/wafp_computed_fps/${timestamp}___${product}___${versionstring}.fp
+ sqlite3 $wafp_fp_db "SELECT * FROM tbl_fprint WHERE product_id =
$product_id" |
+ while read n ; do
+ csum=`echo $n | awk -F'|' '{ print $3 }'`
+ path=`echo $n | awk -F'|' '{ print $4 }'`
+ echo "INSERT INTO tbl_fprint VALUES(NULL, PRODUCT_ID, \"$csum\",
\"$path\")" >>
/tmp/wafp_computed_fps/${timestamp}___${product}___${versionstring}.fp
+ done
+ bzip2
/tmp/wafp_computed_fps/${timestamp}___${product}___${versionstring}.fp
+done
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/utils/generate_fp_sync_list.sh Sat Jan 23
13:51:30 2010
@@ -0,0 +1,22 @@
+#!/bin/bash
+###
+# this script generates the sync list which is to be placed on
+# a wafp update server.
+###
+
+sql=`which sqlite3 2>/dev/null`
+bz2=`which bzip2 2>/dev/null`
+
+if [ "X$sql" = "X" ] ; then
+ echo "ERROR: SQLite3 binary not found in PATH!"
+ exit 0
+fi
+if [ "X$bz2" = "X" ] ; then
+ echo "ERROR: BZip2 binary not found in PATH!"
+ exit 0
+fi
+
+wafp_fp_db="../fprints_wafp.db"
+
+$sql $wafp_fp_db "SELECT DISTINCT timestamp, name, versionstring FROM
tbl_product ASC" > wafp_fp.lst
+$bz2 -v wafp_fp.lst
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/utils/generate_wafp_fingerprint.sh Sat Jan
23 13:51:30 2010
@@ -0,0 +1,144 @@
+#!/bin/bash
+###
+# this is generate_wafp_fingerprint
+# it can be used to generate fingerprints
+# of webapps, to export them and, oh yeah, to import them.
+###
+# its meant to be used with WAFP
+###
+# alpha4 - internal only!
+# by richard sammet (e-axe)
+###
+
+if [ ! -e i_read_README.generate_wafp_fingerprint ] ; then
+ echo "PLEASE READ \"README.generate_wafp_fingerprint\" BEFORE USING THIS
SCRIPT!"
+ exit 0
+fi
+
+if [ -e /tmp/wafp_`basename ${0} .sh`.pid ] ; then
+ echo "ERROR: The PID file (/tmp/wafp_`basename ${0} .sh`.pid) is already
present!"
+ echo "ERROR: You should not run multiple instances of this script on the
same DB at the same time!"
+ echo "ERROR: If this is wrong you can just delete the PID file by hand
and retry execution..."
+ exit 0
+fi
+
+echo $$ > /tmp/wafp_`basename ${0} .sh`.pid
+
+if [ "X$1" == "X" ] ; then
+ echo "USAGE: $0 APPLICATION_PATH|FINGERPRINT_FILE [EXPORT|IMPORT]"
+ exit 0
+fi
+
+function rmpid() {
+ rm -f /tmp/wafp_`basename ${0} .sh`.pid
+}
+
+# all the other stuff should always be there...
+
+sql=`which sqlite3 2>/dev/null`
+fnd=`which find 2>/dev/null`
+ak=`which awk 2>/dev/null`
+sd=`which sed 2>/dev/null`
+
+if [ "X$sql" = "X" ] ; then
+ echo "ERROR: SQLite3 binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$fnd" = "X" ] ; then
+ echo "ERROR: Find binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$ak" = "X" ] ; then
+ echo "ERROR: AWK binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$sd" = "X" ] ; then
+ echo "ERROR: SED binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+
+cur_pwd=`pwd`
+wafp_fp_db="../fprints_wafp.db"
+
+mnext=0
+if [ "X$2" == "XIMPORT" ] ; then
+ if [ -e $1 ] ; then
+ while read LINE ; do
+ if `echo $LINE | grep -q -E '^\-\- NEW_PRODUCT \- START'` ; then
+ mnext=1
+ continue
+ fi
+ if [ $mnext -eq 1 ] ; then
+ mnext=0
+ version=`echo -n $LINE | $ak -F'"' '{ print $4 }'`
+ product=`echo -n $LINE | $ak -F'"' '{ print $2 }'`
+ timestamp=`echo -n $LINE | $ak -F'"' '{ print $6 }'`
+ info=`echo -n $LINE | $ak -F'"' '{ print $8 }'`
+ $sql $cur_pwd/$wafp_fp_db "INSERT INTO tbl_product VALUES(NULL,
\"$product\", \"$version\", \"$timestamp\", \"$info\")"
+ product_id=`$sql $cur_pwd/$wafp_fp_db "SELECT id FROM tbl_product
WHERE name = \"$product\" AND versionstring = \"$version\" LIMIT 1"`
+ continue
+ fi
+ qryp=`echo -n $LINE | $sd s/PRODUCT_ID/$product_id/`
+ $sql $cur_pwd/$wafp_fp_db "$qryp"
+ done < $1
+ rmpid
+ exit 0
+ else
+ echo "FINGERPRINT_FILE($1) not found."
+ rmpid
+ exit 0
+ fi
+fi
+
+if [ -e $1 ] ; then
+
+ product=`basename $1 | $ak -F'-' '{ print $1 }'`
+ version=`basename $1 | $sd s/"^[^\-]\+\-"/""/g`
+ timestamp=`date +%s`
+ info="not used, yet."
+
+ if [ "X$2" == "XEXPORT" ] ; then
+ echo "-- NEW_PRODUCT - START"
+ echo "INSERT INTO tbl_product VALUES(NULL, \"$product\", \"$version\",
\"$timestamp\", \"$info\")"
+ product_id="PRODUCT_ID"
+ else
+ $sql $wafp_fp_db "INSERT INTO tbl_product VALUES(NULL, \"$product\",
\"$version\", \"$timestamp\", \"$info\")"
+ product_id=`$sql $wafp_fp_db "SELECT id FROM tbl_product WHERE name =
\"$product\" AND versionstring = \"$version\" LIMIT 1"`
+ fi
+
+ if [ "X$2" != "XEXPORT" ] ; then
+ echo "switching to $1";
+ fi
+ cd $1;
+
+ file_list=`find ./ -iname '*\.png' -or -iname '*\.gif' -or
-iname '*\.jpg' -or -iname '*\.jpeg' -or -iname '*\.html' -or
-iname '*\.js' -or -iname '*\.xml' -or -iname '*\.swf' -or -iname '*\.txt'
-or -iname '*\.css' -or -iname '*\.htm' -or -iname '*\.xhtml' -or
-iname '*\.pdf' -or -name '*LICENSE*' -or -name '*READ*' -or
-name '*INSTALL*' -or -iname '*\.tpl' -or -iname '*\.ico' -or
-iname '*\.tmpl'`
+
+ for i in $file_list ; do
+ tcsumstr=`md5sum $i | $ak '{ print $1,"|",$2 }' | $sd s/" "/""/g`
+ tcsum=`echo $tcsumstr | $ak -F'|' '{ print $1 }'`
+ tpath=`echo $tcsumstr | $ak -F'|' '{ print $2 }'`
+ if [ "X$2" == "XEXPORT" ] ; then
+ echo "INSERT INTO tbl_fprint VALUES(NULL, $product_id, \"$tcsum\",
\"$tpath\")"
+ else
+ $sql $cur_pwd/$wafp_fp_db "INSERT INTO tbl_fprint VALUES(NULL,
$product_id, \"$tcsum\", \"$tpath\")"
+ fi
+ done
+
+ if [ "X$2" != "XEXPORT" ] ; then
+ echo "switching back to $cur_pwd";
+ fi
+ cd $cur_pwd;
+
+ else
+
+ if [ "X$2" != "XEXPORT" ] ; then
+ echo "no such directory: $1"
+ fi
+
+fi
+
+rmpid
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/utils/online_update.sh Sat Jan 23 13:51:30
2010
@@ -0,0 +1,133 @@
+#!/bin/bash
+###
+# this script performs an online update of the wafp fp db.
+###
+
+if [ -e /tmp/wafp_`basename ${0} .sh`.pid ] ; then
+ echo "ERROR: The PID file (/tmp/wafp_`basename ${0} .sh`.pid) is already
present!"
+ echo "ERROR: You should not run multiple instances of this script on the
same DB at the same time!"
+ echo "ERROR: If this is wrong you can just delete the PID file by hand
and retry execution..."
+ exit 0
+fi
+
+echo $$ > /tmp/wafp_`basename ${0} .sh`.pid
+
+wafp_fp_db="../fprints_wafp.db"
+update_site="http://208.111.34.127/wafp_update"
+update_list="wafp_fp.lst.bz2"
+update_source="$update_site/wafp_fps"
+
+function rmpid() {
+ rm -f /tmp/wafp_`basename ${0} .sh`.pid
+}
+
+function usage() {
+ echo "USAGE: $0 --update|--show"
+ echo "--"
+ echo " --update updates your local database from the online repository."
+ echo " --show shows which fingerprints would be updated."
+ rmpid
+ exit 0
+}
+
+if [ "X$1" = "X" ] ; then
+ usage
+fi
+
+sql=`which sqlite3 2>/dev/null`
+fnd=`which find 2>/dev/null`
+ak=`which awk 2>/dev/null`
+sd=`which sed 2>/dev/null`
+bz2=`which bunzip2 2>/dev/null`
+wg=`which wget 2>/dev/null`
+
+if [ "X$sql" = "X" ] ; then
+ echo "ERROR: SQLite3 binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$bz2" = "X" ] ; then
+ echo "ERROR: BunZip2 binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+
+if [ "X$fnd" = "X" ] ; then
+ echo "ERROR: Find binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$ak" = "X" ] ; then
+ echo "ERROR: AWK binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$sd" = "X" ] ; then
+ echo "ERROR: SED binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+if [ "X$wg" = "X" ] ; then
+ echo "ERROR: WGet binary not found in PATH!"
+ rmpid
+ exit 0
+fi
+
+if [ "X$1" != "X--show" ] && [ "X$1" != "X--update" ] ; then
+ usage
+fi
+
+# collecting the local db fprints...
+rm -f /tmp/wafp_fp_local.lst
+for i in `$sql $wafp_fp_db "SELECT DISTINCT timestamp, name, versionstring
FROM tbl_product ASC"` ; do
+ echo $i >> /tmp/wafp_fp_local.lst
+done
+
+# fetching the latest fprint repository
+rm -f /tmp/wafp_fp.lst
+rm -f /tmp/wafp_fp.lst.bz2
+$wg -O /tmp/wafp_fp.lst.bz2 $update_site/$update_list
+$bz2 -v /tmp/wafp_fp.lst.bz2
+
+cnt=`diff /tmp/wafp_fp.lst /tmp/wafp_fp_local.lst | grep -E '^<' | awk '{
print $2 }' | wc -l`
+if [ $cnt -eq 0 ] ; then
+ echo ""
+ echo -e "\033[4mNothing to be done, you are up-to-date! ;)\033[0m"
+ rmpid
+ exit 0
+fi
+
+if [ "X$1" = "X--show" ] ; then
+ echo ""
+ echo -e "\033[1mThe following FingerPrints would be installed:\033[0m"
+ sleep 1;
+ diff /tmp/wafp_fp.lst /tmp/wafp_fp_local.lst | grep -E '^<' | awk '{
print $2 }'
+ rmpid
+ exit 0
+fi
+
+# fetching new fps...
+echo ""
+echo -e "\033[1mDownloading latest FingerPrints:\033[0m"
+sleep 1;
+rm -rf /tmp/wafp_fps
+mkdir /tmp/wafp_fps
+for i in `diff /tmp/wafp_fp.lst /tmp/wafp_fp_local.lst | grep -E '^<' |
awk '{ print $2 }'` ; do
+ $wg -O /tmp/wafp_fps/`echo $i | $sd s/\|/___/g`.fp.bz2
$update_source/`echo $i | $sd s/\|/___/g`.fp.bz2
+done
+
+echo ""
+echo -e "\033[1mUnpacking downloaded FingerPrints:\033[0m"
+for i in `ls /tmp/wafp_fps/*.bz2` ; do
+ $bz2 -v $i
+done
+
+echo ""
+echo -e "\033[1mAdding FingerPrints to your local database:\033[0m"
+# adding the newly downloaded fps to the db
+for i in `ls /tmp/wafp_fps/*.fp` ; do
+ echo "adding: `basename $i .fp`"
+ ./generate_wafp_fingerprint.sh $i IMPORT
+done
+
+rmpid
=======================================
--- /dev/null
+++ /branches/e-axe_redirecting/wafp.rb Sat Jan 23 13:51:30 2010
@@ -0,0 +1,873 @@
+#!/usr/bin/ruby
+################################################################################
+# WAFP - Web Application Finger Printer
+################################################################################
+# 0.01-26c3 - 2009.12.28 - by Richard Sammet (e-axe)
richard...@gmail.com
+################################################################################
+# inspired by:
+# "http://sucuri.net/?page=docs&title=webapp-version-detection"
+################################################################################
+# Information and latest version available at:
+# http://mytty.org/wafp/
+################################################################################
+# This file is part of WAFP.
+#
+# WAFP is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# WAFP is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with WAFP; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# http://www.gnu.org/licenses/gpl.txt
+################################################################################
+# KNOWN BUGS:
+# ruby reports a deadlock -> http://redmine.ruby-lang.org/issues/show/1471
+# - looks like this is ruby version dependant (occurred with 1.8.6)
+# TODO:
+# finish proxy support
+################################################################################
+
+# the lib requirements
+
+begin
+ # this is required for all the people using rubygems on their systems
+ require 'rubygems'
+rescue LoadError
+end
+require 'getoptlong'
+require 'sqlite3'
+# this is a modified version of net/http
+require "lib/wafp_http.rb"
+require "lib/wafp_https.rb"
+# this is a modified version of pidify
+require "lib/wafp_pidify.rb"
+require 'uri'
+require 'thread'
+require 'timeout'
+require 'digest/md5'
+
+# some constants
+CODENAME = 'WAFP';
+WAFPVERSION = '0.01-26c3';
+AUTHOR = 'Richard Sammet (e-axe)';
+CONTACT = 'richard...@gmail.com';
+WEBSITE = 'http://mytty.org/wafp/';
+# the databases
+FDB = 'fprints_wafp.db'
+SDB = 'scan_wafp.db'
+# predefined http headers
+USERAGENT = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10)
Gecko/2009050223 Gentoo Firefox/3.0.10'
+
+# check if wafp is already running
+if Pidify.running? or Pidify.pid_exists? then
+ puts "ERROR: #{$0} is already running!"
+ puts "ERROR: If it is not, you can remove the PID file
(#{Pidify.pid_file}) by hand."
+ puts "ERROR: PLEASE DO NOT START WAFP MULTIPLE TIMES WITH THE SAME DB ON
THE SAME MACHINE!"
+ exit 0
+else
+ Pidify.start
+end
+
+# globals
+@db = nil
+@memdb = nil
+@lowmem = false
+@verbose = false
+@debug = false
+@quiet = false
+@idcount = 10
+@tstamp = Time.now.to_i
+@scanid = "#{$$}#{@tstamp}"
+@cancel_info = Array.new()
+# you can add all the header fields you want right here...
+@headers = {
+ 'User-Agent' => USERAGENT,
+ #'X-Testme' => "VOID"
+ }
+
+# trapping the INT signal, cleaning up and exit
+trap("INT") do
+ tries = 0
+ puts "received sig-int ... cleaning up." if !@quiet
+ # well, lets just close the db if its open and open it again for the
cleaning
+ begin
+ tries = tries + 1
+ dbclose(@db)
+ rescue
+ if tries > 25 then
+ puts "ERROR: We were not able to clean the database! Exiting
anyways!"
+ Pidify.stop
+ exit(-1)
+ end
+ sleep 0.1
+ retry
+ end
+ if @cancel_info[1] == nil then
+ @db = dbopen(SDB)
+ @db.execute( "DELETE FROM tbl_results WHERE store_id = (SELECT id FROM
tbl_store WHERE name = \"#{@cancel_info[0]}\" LIMIT 1)" )
+ puts "DEBUG: executed SQL query: DELETE FROM tbl_results WHERE
store_id = (SELECT id FROM tbl_store WHERE name = \"#{@cancel_info[0]}\"
LIMIT 1)" if @debug
+ @db.execute( "DELETE FROM tbl_store WHERE name =
\"#{@cancel_info[0]}\"" )
+ puts "DEBUG: executed SQL query: DELETE FROM tbl_store WHERE name =
\"#{@cancel_info[0]}\"" if @debug
+ dbclose(@db)
+ puts "VERBOSE: temp. scan data for scan \"#{@cancel_info[0]}\"
deleted." if @verbose
+ end
+
+ Pidify.stop
+ exit 0
+end # trap()
+
+# print the USAGE message and exit
+def usage()
+
+ puts "USAGE: #{$0} [Options] {URL}"
+ puts "--"
+ puts " -p, --product STRING a string which represents the name of
the product to check for;"
+ puts " STRING can be something like:
\"wordpress\""
+ puts " -v, --pversion STRING a string which represents the versions
of the product to check for;"
+ puts " STRING can be something like:
\"2.2.1\" or \"%.2\" or \"1.%\"."
+ puts " -P, --dump-products STRING this will dump all products for which
fingerprints are available;"
+ puts " STRING can be something like: \"%bb%\"
which will select all products"
+ puts " having bb|BB in their name."
+ puts " -s, --store STRING write the fetched data to the database
for later use;"
+ puts " STRING is used as an identifier."
+ puts " -f, --fetch fetch only - do not fingerprint the
app."
+ puts " (mostly used in conjunction with -s)"
+ puts " -l, --list STRING list the stored data archives
containing STRING."
+ puts " STRING is optional in this case."
+ puts " -d, --dry STRING perform the fingerprint on the stored
data STRING instead of fetching it."
+ puts " -t, --threads INT this is the count of threads to use.
[8]"
+# puts " --proxy STRING a STRING which holds the proxy
information and optional"
+# puts " user/password combination;"
+# puts " e.g.
http://user:pa...@proxy.host.org:8080/"
+# puts " STRING can also be ENV - http_proxy
environ variable."
+ puts " --user-agent STRING a STRING which holds the User-Agent
headerfield contents."
+ puts " --outlines INT number of results to print. [10]"
+ puts " --timeout INT connection timeout in seconds. [10]"
+ puts " --retries INT maximum retries per file to fetch. [3]"
+ puts " --any this causes wafp to fetch all files
known by fingerprints of all products."
+ puts " --low-mem this causes wafp to NOT load the
fingerprint database to the memory."
+ puts " --verbose turns on verbose output."
+ puts " --debug turns on debug output."
+ puts " --quiet output off - besides the final
results."
+ puts " --dbinfo prints some database stats."
+ puts " --version print WAFP version and exit."
+ puts " -h, --help print this help and exit."
+ puts ""
+ puts "EXAMPLES:"
+ puts " #{$0} -p 'wordpress' -v '2%' http://blog.example.com/"
+ puts " #{$0} -f -t 32 -s phpmy-save01 -p 'phpmyadmin' -v '1.1.%'
https://user:pa...@www.example.com/phpmyadmin/"
+ puts " #{$0} -d phpmy-save01 -p 'phpmyadmin' -v '1.1.%'"
+
+ Pidify.stop
+ exit 0
+
+end # usage()
+
+# print the version information and exit
+def version()
+
+ puts "version: \t#{WAFPVERSION}"
+ puts "codename: \t#{CODENAME}"
+ puts "author: \t#{AUTHOR}"
+ puts "website: \t#{WEBSITE}"
+
+ Pidify.stop
+ exit 0
+
+end # version()
+
+# to make this a little shorter...
+def dbopen(db)
+
+ return @memdb if @lowmem == false and db == FDB and @memdb != nil
+
+ return SQLite3::Database.new( db )
+
+end # dbopen()
+
+# to make this a little shorter...
+def dbclose(db)
+
+ return nil if @lowmem == false and db == @memdb
+
+ db.close if !db.closed?
+
+ return nil # well, you are welcome to add some error handling ;)
+
+end # dbclose()
+
+# loads a sqlite3 db into memory
+def loadb(db)
+
+ puts "VERBOSE: loading the fingerprint database to the ram..." if
@verbose
+ @memdb = SQLite3::Database.new( ":memory:" )
+ qry = db.execute( "SELECT sql FROM sqlite_master WHERE sql NOT NULL" )
+ @memdb.execute( "BEGIN" )
+ qry.each do |l|
+ @memdb.execute( l.to_s.gsub(/\r?\n/, " ") )
+ end
+ @memdb.execute( "COMMIT" )
+
+ @memdb.execute( "ATTACH DATABASE '#{FDB}' as fprintdb" )
+
+ qry = @memdb.execute( "SELECT name FROM fprintdb.sqlite_master WHERE
type='table'" )
+ @memdb.execute( "BEGIN" )
+ qry.each do |l|
+ @memdb.execute( "INSERT INTO main.#{l.to_s} SELECT * from
fprintdb.#{l.to_s}" )
+ end
+ @memdb.execute( "COMMIT" )
+ @memdb.execute( "DETACH DATABASE fprintdb" )
+
+ dbclose(db)
+
+ return @memdb
+
+end # loadb()
+
+# this func dumps the products and versions available in our database
+# and yes, i know that this is not sql injection safe - wuuhhhaaaa hack
yourself ;)
+def dump(dproducts)
+
+ @db = dbopen(FDB)
+ products = @db.execute( "SELECT DISTINCT name FROM tbl_product WHERE
name LIKE \"#{dproducts}\" ORDER BY name ASC" )
+ puts "DEBUG: executed SQL query: SELECT DISTINCT name FROM tbl_product
WHERE name LIKE \"#{dproducts}\" ORDER BY name ASC" if @debug
+
+ products.each do |pname|
+ cc = false # a little helper for a more sane output
+ puts "#{pname}:"
+ @db.execute( "SELECT DISTINCT versionstring FROM tbl_product WHERE
name = \"#{pname}\" ORDER BY versionstring ASC" ) do |vstring|
+ if cc == true then print ", " else cc = true end
+ print "#{vstring}"
+ end
+ puts "DEBUG: executed SQL query: SELECT DISTINCT versionstring FROM
tbl_product WHERE name = \"#{pname}\" ORDER BY versionstring ASC" if @debug
+ puts ""
+ end
+
+ dbclose(@db)
+
+ Pidify.stop
+ exit 0
+
+end # dump()
+
+# just list the stored scans filtered by "list"
+def liststore(list)
+
+ @db = dbopen(SDB)
+ scans = @db.execute( "SELECT DISTINCT name FROM tbl_store WHERE name
LIKE \"#{list}\" ORDER BY name ASC" )
+ puts "DEBUG: executed SQL query: SELECT DISTINCT name FROM tbl_store
WHERE name LIKE \"#{list}\" ORDER BY name ASC" if @debug
+
+ scans.each do |scan|
+ puts scan
+ end
+
+ dbclose(@db)
+
+ Pidify.stop
+ exit 0
+
+end # liststore()
+
+# print some stats of our databases
+def dbinfo()
+
+ @db = dbopen(FDB)
+
+ puts "_WAFP Database Stats_"
+ pcnt = @db.execute( "SELECT DISTINCT name FROM tbl_product" )
+ puts "DEBUG: executed SQL query: SELECT DISTINCT name FROM tbl_product"
if @debug
+ puts "Number of products: #{pcnt.length}"
+ vcnt = @db.get_first_value( "SELECT count(versionstring) FROM
tbl_product" )
+ puts "DEBUG: executed SQL query: SELECT count(versionstring) FROM
tbl_product" if @debug
+ puts "Number of versions: #{vcnt}"
+ fcnt = @db.get_first_value( "SELECT count(*) FROM tbl_fprint" )
+ puts "DEBUG: executed SQL query: SELECT count(*) FROM tbl_fprint" if
@debug
+ puts "Number of fingerprint checks: #{fcnt}"
+
+ dbclose(@db)
+ @db = dbopen(SDB)
+
+ scnt = @db.get_first_value( "SELECT count(*) FROM tbl_store" )
+ puts "Number of stored scans: #{scnt}"
+ fcnt = @db.get_first_value( "SELECT count(*) FROM tbl_results" )
+ puts "Number of stored fingerprint checks: #{fcnt}"
+
+ dbclose(@db)
+
+ Pidify.stop
+ exit 0
+
+end # dbinfo()
+
+def identify_product(uri, pproxy, threads, tout, rtry, save)
+
+ paths = Hash.new
+ products = Hash.new
+ matches = Hash.new
+ tmatches = Hash.new
+ idproduct = nil
+ idmatches = nil
+
+ puts "Collecting and fetching the files we need to identify the
product ..." if !@quiet
+
+ @db = dbopen(FDB)
+
+ products = @db.execute( "SELECT DISTINCT name FROM tbl_product" )
+ products.each do |prod|
+ paths[prod] = @db.execute( "SELECT DISTINCT path FROM tbl_fprint WHERE
product_id IN(SELECT id FROM tbl_product WHERE name = \"#{prod}\") GROUP BY
path ORDER BY count(path) DESC LIMIT #{@idcount}" )
+ end
+
+ dbclose(@db)
+
+ paths.each do |prod, path|
+ print "\nChecking for product: " + prod.to_s + "\n" if @verbose
+ print "For paths: " + path.to_s + "\n" if @debug
+ matches = check(scan_name = nil, product = prod.to_s, pversion = "%",
idrun = true, fetch(path, uri, pproxy, threads, tout, rtry, nil, save))
+ tmatches = tmatches.merge(matches)
+ end
+
+ tmatches = tmatches.sort {|a,b| -1*(a[1][2]<=>b[1][2]) }
+ idproduct, idmatches = tmatches[0]
+ products.each do |prod|
+ if idproduct =~ /^#{prod}-/ and idmatches[2].to_f > 0.00 then
+ printf(STDOUT, "\nIdentified Product: %s (%.2f %%)\n", prod,
idmatches[2].to_f) if !@quiet
+ return prod
+ end
+ end
+
+ puts ""
+ puts "WARNING: The auto product identification was not able to identify
the product on the"
+ puts "WARNING: targeted site. You can make use of the --any option or
guess the product"
+ puts "WARNING: yourself and add -p paramater."
+
+ Pidify.stop
+ exit 0
+
+end # identify_product()
+
+# select the files we need to fetch from the database
+def tofetch(product, pversion)
+
+ puts "Collecting the files we need to fetch ..." if !@quiet
+
+ @db = dbopen(FDB)
+ paths = @db.execute( "SELECT DISTINCT path FROM tbl_fprint WHERE
product_id IN (SELECT DISTINCT id FROM tbl_product WHERE name LIKE
\"#{product}\" AND versionstring LIKE \"#{pversion}\" ORDER BY name ASC)
ORDER BY path ASC" )
+ puts "DEBUG: executed SQL query: SELECT DISTINCT path FROM tbl_fprint
WHERE product_id IN (SELECT DISTINCT id FROM tbl_product WHERE name LIKE
\"#{product}\" AND versionstring LIKE \"#{pversion}\" ORDER BY name ASC)
ORDER BY path ASC" if @debug
+
+ dbclose(@db)
+
+ if paths.length == 0 then
+ puts "WARNING: There are not fingerprints matching your options!"
if !@quiet
+ Pidify.stop
+ exit 0
+ end
+
+ paths
+
+end # tofetch()
+
+# this function generates the sorted result output
+def genoutput(hashes, lines)
+
+ n = 0
+
+ matches = hashes[0]
+ rcmatches = hashes[1]
+
+ # sort the rc/matches hash by percentage of match
+ matches = matches.sort {|a,b| -1*(a[1][2]<=>b[1][2]) }
+ if rcmatches != nil then
+ rcmatches = rcmatches.sort {|a,b| -1*(a[1]<=>b[1]) }
+ end
+
+ # printing the results
+ puts "" if !@quiet
+ puts " found the following matches (limited to #{lines.to_i}):"
if !@quiet
+
print "+-------------------------------------------------------------+\n"
if !@quiet
+ matches.each do |pname,cntp|
+ printf(STDOUT, " %-35s\t%4s / %-4s (%.2f%%)\n", pname, cntp[0],
cntp[1], cntp[2].to_f)
+ break if (n += 1) >= lines.to_i
+ end
+
print "+-------------------------------------------------------------+\n"
if !@quiet
+ puts " #{CODENAME} #{WAFPVERSION} - - - - - - - - - #{WEBSITE}"
if !@quiet
+
+ if @verbose and rcmatches != nil then
+ puts ""
+ puts "VERBOSE: Returncode stats:"
+ rcmatches.each do |key, val|
+ puts "VERBOSE: Ret-Code\t#{key}\t##{val}"
+ end
+ end
+
+end # genoutput()
+
+# fetch required files multithreaded and store the
+# paths, checksums and return codes inside the db
+def fetch(paths, uri, pproxy, mthreads, tout, rtry, scan_name, save)
+
+ @db = dbopen(SDB)
+ ts = Array.new
+ m = Mutex.new
+ md5sums = Array.new
+ threads = 0
+ user = nil
+ pass = nil
+ puser = nil
+ ppass = nil
+ phost = nil
+ pport = nil
+
+ turi = URI.parse(uri)
+
+ if turi.userinfo then
+ user, pass = turi.userinfo.split(/:/)
+ puts "detected username (#{user}) and password (#{"*" * pass.length})
for basic-auth." if @verbose
+ end
+ # TODO: the following proxy stuff gets not used yet
+ if pproxy then
+ phost = pproxy.host
+ pport = pproxy.port
+ puts "detected proxy host (#{phost}) and port (#{pport})." if @verbose
+ if pproxy.userinfo then
+ puser, ppass = pproxy.userinfo.split(/:/)
+ puts "detected proxy username (#{puser}) and password (#{"*" *
ppass.length}) for basic-auth." if @verbose
+ end
+ end
+
+ if save then
+ puts "Fetching needed files (##{paths.length}), calculating checksums
and storing the results to the database:" if !@quiet
+ elsif !save and @verbose then
+ puts "Fetching needed files (##{paths.length}) and calculating
checksums:" if !@quiet
+ end
+
+ paths.each do |path|
+ if threads < mthreads then
+ m.synchronize do
+ threads += 1
+ puts "VERBOSE: running with #{threads} threads!" if @debug
+ end
+ ts << Thread.new(path) do |path|
+ res = nil
+ req = nil
+ rrtry = 0
+ md5sum = nil
+ path = path.to_s.gsub(/^\.\//, '')
+ path = turi.path !~ /\/$/?("#{turi.path}/"):(turi.path), path
+ # fetching the static files...
+ begin
+ Timeout::timeout(tout) do
+ # TODO: add proxy support
+ 1.times do {
+ req = Net::HTTP::Get.new(path, @headers)
+ http = Net::HTTP.new(turi.host, turi.port)
+ req.basic_auth(user, pass) if user and pass
+ if turi.scheme == "https" then
+ http.use_ssl = true
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ res = http.request(req)
+ puts "DEBUG: fetched #{path}" if @debug
+ if res == Net::HTTPRedirection then
+ path = URI.parse(res['location']).path
+ redo
+ end
+ }
+ end
+ rescue Timeout::Error
+ if rrtry < rtry then
+ rrtry += 1
+ puts "\nVERBOSE: request for \"#{path}\" timed out for
#{rrtry} times - retrying..." if @verbose
+ retry
+ else
+ puts "\nWARNING: request for \"#{path}\" timed out for #{rtry}
times!" if !@quiet
+ end
+ rescue =>e
+ if rrtry < rtry then
+ rrtry += 1
+ puts "\nVERBOSE: request for \"#{path}\" produced \"#{e}\" for
#{rrtry} times - retrying..." if @verbose
+ retry
+ else
+ puts "\nWARNING: request for \"#{path}\" produced \"#{e}\" for
#{rtry} times!" if !@quiet
+ end
+ rescue StandardError => msg
+ puts "ERROR: #{msg}" if !@quiet
+ Pidify.stop
+ exit 0
+ end
+ puts "DEBUG: res.code = #{res.code}" if @debug
+ puts "DEBUG: res.body = #{res.body}" if @debug
+ # checksum stuff
+ begin
+ md5sum = Digest::MD5.hexdigest(res.body.to_s)
+ puts "DEBUG: md5sum = #{md5sum}" if @debug
+ rescue
+ puts "WARNING: an error occoured while generating the md5sum of
path = #{path}!" if !@quiet
+ end
+ m.synchronize do
+ if save then
+ # inserting data (csum, path, return-code) into the db
+ @db.execute( "INSERT INTO tbl_results VALUES(NULL, (SELECT id
FROM tbl_store WHERE name = \"#{scan_name}\" LIMIT 1), \"#{md5sum}\",
\"#{path}\", \"#{res.code}\")" )
+ else
+ md5sums.push(md5sum)
+ end
+ end
+ if save then
+ puts "DEBUG: executed SQL query: INSERT INTO tbl_results
VALUES(NULL, (SELECT id FROM tbl_store WHERE name = \"#{scan_name}\" LIMIT
1), \"#{md5sum}\", \"#{path}\", \"#{res.code}\")" if @debug
+ end
+ m.synchronize do
+ print "." if !@quiet
+ STDOUT.flush if !@quiet
+ threads -= 1
+ end
+ end
+ else
+ sleep 0.1
+ redo
+ end
+ end
+
+ ts.each do |th| th.join end
+
+ puts "" if save or @verbose
+
+ dbclose(@db)
+
+ return md5sums if !save
+
+end # fetch()
+
+# this func finally performs the checks
+def check(scan_name, product, pversion, idrun, csums)
+
+ matches = Hash.new()
+ rcmatches = Hash.new()
+ csumstr = ""
+ ignorecnt = 0
+ truecnt = 0
+ retcodes = Array.new()
+
+ if !idrun then
+ @db = dbopen(SDB)
+ csums = @db.execute( "SELECT csum, retcode FROM tbl_results WHERE
store_id = (SELECT id FROM tbl_store WHERE name = \"#{scan_name}\" LIMIT
1)" )
+ puts "DEBUG: executed SQL query: SELECT csum FROM tbl_results WHERE
store_id = (SELECT id FROM tbl_store WHERE name = \"#{scan_name}\" LIMIT
1)" if @debug
+
+ dbclose(@db)
+ end
+
+ csums.each_index do |x|
+ retcodes.push(csums[x][1])
+ if !idrun then
+ csumstr = "#{csumstr}#{csums[x][0]}\",\""
+ else
+ csumstr = "#{csumstr}#{csums[x]}\",\""
+ end
+ end
+
+ @db = dbopen(FDB)
+ versions = @db.execute( "SELECT versionstring FROM tbl_product WHERE
name LIKE \"#{product}\" AND versionstring LIKE \"#{pversion}\"" )
+
+ puts "Checking gathered/stored checksums (##{csums.length}) against the
selected product (#{product}) versions (##{versions.length}) checksums:"
if !@quiet and idrun == false
+ puts "DEBUG: executed SQL query: SELECT versionstring FROM tbl_product
WHERE name LIKE \"#{product}\" AND versionstring LIKE \"#{pversion}\"" if
@debug
+
+ versions.each do |ver|
+ m = @db.get_first_value( "SELECT count(path) FROM tbl_fprint WHERE
product_id = (SELECT DISTINCT id FROM tbl_product WHERE name LIKE
\"#{product}\" AND versionstring = \"#{ver}\") AND csum IN
(\"#{csumstr}\")" )
+ puts "DEBUG: executed SQL query: SELECT count(path) FROM tbl_fprint
WHERE product_id IN (SELECT DISTINCT id FROM tbl_product WHERE name LIKE
\"#{product}\" AND versionstring = \"#{ver}\") AND csum IN
(\"#{csumstr}\")" if @debug
+ actual_product = @db.get_first_value( "SELECT name FROM tbl_product
WHERE name LIKE \"#{product}\" AND versionstring = \"#{ver}\" LIMIT 1" )
+ puts "DEBUG: executed SQL query: SELECT name FROM tbl_product WHERE
name LIKE \"#{product}\" AND versionstring = \"#{ver}\" LIMIT 1" if @debug
+ c = @db.get_first_value( "SELECT count(path) FROM tbl_fprint WHERE
product_id = (SELECT DISTINCT id FROM tbl_product WHERE name LIKE
\"#{product}\" AND versionstring = \"#{ver}\")" )
+ puts "DEBUG: executed SQL query: SELECT count(path) FROM tbl_fprint
WHERE product_id IN (SELECT DISTINCT id FROM tbl_product WHERE name LIKE
\"#{product}\" AND versionstring = \"#{ver}\")" if @debug
+ if !idrun then
+ p = m.to_f / (c.to_f / 100.00)
+ matches["#{actual_product}-#{ver}"] = [m.to_i, c.to_i, p.to_f]
+ else
+ p = m.to_f / (@idcount / 100.00)
+ matches["#{actual_product}-#{ver}"] = [m.to_i, @idcount, p.to_f]
+ end
+ print "." if !@quiet
+ STDOUT.flush if !@quiet
+ end
+
+ dbclose(@db)
+
+ if !idrun then
+ # doing some retcode stat calculation if verbose is enabled
+ if @verbose then
+ retcodes.sort.uniq.each do |code|
+ tcnt = 0
+ retcodes.each do |tcode|
+ if tcode == code then
+ tcnt += 1
+ end
+ end
+ rcmatches["#{code}"] = tcnt
+ end
+ end
+ puts ""
+
+ return [matches, rcmatches]
+ else
+ return matches
+ end
+
+end # check()
+
+# the main() function
+def main()
+
+ # if the arguments were omitted there is no need to go any further
+ if ARGV.length == 0 then
+ puts "What should I do? (try --help)"
+ Pidify.stop
+ exit 0
+ end
+
+ # check if our DBs are existant
+ if !File.exist?(FDB) then
+ puts "ERROR: The FingerPrint Database File (#{FDB}) can not be found!"
+ Pidify.stop
+ exit 0
+ end
+
+ if !File.exist?(SDB) then
+ puts "ERROR: The Scan Database File (#{SDB}) can not be found!"
+ Pidify.stop
+ exit 0
+ end
+
+ # defining the valid options
+ opts = GetoptLong.new(
+ [ '--product', '-p', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--pversion', '-v', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--dump-products', '-P', GetoptLong::OPTIONAL_ARGUMENT ],
+ [ '--store', '-s', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--fetch', '-f', GetoptLong::NO_ARGUMENT ],
+ [ '--list', '-l', GetoptLong::OPTIONAL_ARGUMENT ],
+ [ '--dry', '-d', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--threads', '-t', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--proxy', GetoptLong::OPTIONAL_ARGUMENT ],
+ [ '--user-agent', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--outlines', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--timeout', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--retries', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--any', GetoptLong::NO_ARGUMENT ],
+ [ '--low-mem', GetoptLong::NO_ARGUMENT ],
+ [ '--verbose', GetoptLong::NO_ARGUMENT ],
+ [ '--debug', GetoptLong::NO_ARGUMENT ],
+ [ '--quiet', GetoptLong::NO_ARGUMENT ],
+ [ '--dbinfo', GetoptLong::NO_ARGUMENT ],
+ [ '--version', GetoptLong::NO_ARGUMENT ],
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ]
+ )
+
+ # well, thats because most users are lazy when it comes to
+ # the correct order of the command opts ;)
+ opts.ordering = GetoptLong::PERMUTE
+
+ # vars that help to handle the arguments
+ product = nil
+ pversion = nil
+ dproducts = nil
+ store = nil
+ fetcho = false
+ list = nil
+ dry = nil
+ threads = 8
+ proxy = nil
+ pproxy = nil
+ uagent = nil
+ lines = 10
+ tout = 10
+ rtry = 3
+ any = false
+ # @verbose = false # already defined global
+ # @debug = false # already defined global
+ # @quiet = false # already defined global
+ uri = nil
+ scan_name = nil
+ sinfo = "not used, yet."
+
+ # opt processing
+ begin
+ opts.each do |opt, arg|
+ case opt
+ when '--product'
+ product = arg
+ when '--pversion'
+ pversion = arg
+ when '--dump-products'
+ if arg == '' then dproducts = '%' else dproducts = arg end
+ dump(dproducts) # exit 0
+ when '--store'
+ store = arg
+ when '--fetch'
+ fetcho = true
+ when '--list'
+ if arg == '' then list = '%' else list = arg end
+ liststore(list) # exit 0
+ when '--dry'
+ dry = arg
+ when '--threads'
+ threads = arg.to_i
+ when '--proxy'
+ if arg == "ENV" then proxy = ENV['http_proxy'] else proxy = arg
end
+ when '--user-agent'
+ uagent = arg
+ when '--outlines'
+ lines = arg
+ when '--timeout'
+ tout = arg.to_i
+ when '--retries'
+ rtry = arg.to_i
+ when '--any'
+ any = true
+ when '--low-mem'
+ @lowmem = true
+ when '--verbose'
+ @verbose = true
+ when '--debug'
+ @debug = true
+ when '--quiet'
+ @quiet = true
+ when '--dbinfo'
+ dbinfo() # exit 0
+ when '--version'
+ version() # exit 0
+ when '--help'
+ usage() # exit 0
+ end
+ end
+ rescue # we do not care whats wrong with the opts - just yell! ;)
(getoptlong should handle this)
+ puts ""
+ usage()
+ end
+
+ if threads < 1 || threads > 256 then
+ puts "ERROR: the thread count should be greater than 1 and not higher
than 256!"
+ Pidify.stop
+ exit 0
+ end
+
+ if tout < 1 || tout > 300 then
+ puts "ERROR: the timeout should be greater than 0 and not higher than
300!"
+ Pidify.stop
+ exit 0
+ end
+
+ if rtry < 0 || rtry > 256 then
+ puts "ERROR: the retry count should not be lower than 0 and not higher
than 256!"
+ Pidify.stop
+ exit 0
+ end
+
+ if store then
+ scan_name = "#{store}_#{@scanid}#{@tstamp}"
+ else
+ scan_name = "#{@scanid}#{@tstamp}"
+ end
+
+ if uagent then
+ puts "VERBOSE: replacing default User-Agent with \"#{uagent}\" ..." if
@verbose
+ @headers['User-Agent'] = uagent
+ end
+
+ # ok, lets continue with the program flow...
+ # check if its a dry or live run - otherwise there is nothing left to do
+ # at this point of execution!
+ if (ARGV.length == 1) then # is there an uri in one of our opts?
+ given_url = ARGV[0]
+ begin
+ uri = URI.parse(given_url)
+ rescue
+ puts "ERROR: uri parsing failed!"
+ Pidify.stop
+ exit 0
+ end
+ scan_name = "#{scan_name}_#{uri.scheme}#{uri.host}#{uri.path}"
+ @cancel_info[0] = scan_name
+ @cancel_info[1] = nil # its not a dry run
+ # TODO: the following proxy stuff is not used yet
+ if proxy then
+ begin
+ pproxy = URI.parse(proxy)
+ rescue
+ puts "ERROR: proxy parsing failed!"
+ Pidify.stop
+ exit 0
+ end
+ if pproxy.scheme !~ /^(http|https)$/ then
+ puts "ERROR: unknown proxy protocol \"#{pproxy.scheme}\"!"
+ Pidify.stop
+ exit 0
+ end
+ end
+ if uri.scheme =~ /^(http|https)$/ then
+ # if the low-mem option is not enabled we load the whole fprint db
to the ram
+ if @lowmem == false
+ @memdb = loadb(dbopen(FDB))
+ end
+ if product == nil and any == true then
+ product = '%'
+ elsif product == nil and any == false and pversion == nil then
+ product = identify_product(given_url, pproxy, threads, tout, rtry,
save = false)
+ end
+ pversion = '%' if pversion == nil
+ puts "DEBUG: calling tofetch(\"#{product}\", \"#{pversion}\")" if
@debug
+ paths = tofetch(product, pversion)
+ else
+ puts "ERROR: unknown protocol \"#{uri.scheme}\"!"
+ Pidify.stop
+ exit 0
+ end
+ # adding a uniq scan entry to the database
+ @db = dbopen(SDB)
+ @db.execute( "INSERT INTO tbl_store VALUES(NULL, \"#{scan_name}\",
\"#{@tstamp}\", \"#{sinfo}\")" )
+ puts "DEBUG: executed SQL query: INSERT INTO tbl_store VALUES(NULL,
\"#{scan_name}\", \"#{@tstamp}\", \"#{sinfo}\")" if @debug
+ dbclose(@db)
+ puts "DEBUG: calling fetch(\"#{paths}\", \"#{given_url}\",
\"#{pproxy}\", \"#{threads}\", \"#{tout}\", \"#{rtry}\", \"#{scan_name}\",
\"false\")" if @debug
+ fetch(paths, given_url, pproxy, threads, tout, rtry, scan_name, save =
true)
+ if !fetcho then
+ puts "DEBUG: calling check(\"#{scan_name}\", \"#{product}\",
\"#{pversion}\")" if @debug
+ genoutput(check(scan_name, product, pversion, idrun = false, nil),
lines)
+ else
+ puts "Fetching done - terminating." if !@quiet
+ end
+ elsif (dry != nil)
+ product = '%' if product == nil
+ pversion = '%' if pversion == nil
+ genoutput(check(dry, product, pversion, idrun = false, nil), lines)
+ else
+ puts "ERROR: if you don't specify an URI then you should use -d/--dry!"
+ puts "ERROR: otherwise there is nothing I can do for you!"
+ Pidify.stop
+ exit 0
+ end
+
+ # if the job is done and the user selected to store this run
+ # we will leave it in the db. otherwise it will be deleted.
+ if !store and !dry then
+ puts "VERBOSE: deleting the temporary database entries for scan
\"#{scan_name}\" ..." if @verbose
+ @db = dbopen(SDB)
+ @db.execute( "DELETE FROM tbl_results WHERE store_id = (SELECT id FROM
tbl_store WHERE name = \"#{scan_name}\" LIMIT 1)" )
+ puts "DEBUG: executed SQL query: DELETE FROM tbl_results WHERE
store_id = (SELECT id FROM tbl_store WHERE name = \"#{scan_name}\" LIMIT
1)" if @debug
+ @db.execute( "DELETE FROM tbl_store WHERE name = \"#{scan_name}\"")
+ puts "DEBUG: executed SQL query: DELETE FROM tbl_store WHERE name =
\"#{scan_name}\"" if @debug
+ dbclose(@db)
+ elsif store and !dry
+ puts "The scan got stored to the database with the name
\"#{scan_name}\"." if !@quiet
+ end
+
+end # main()
+
+# lets start by executing the main function ;)
+main()
+
+Pidify.stop
=======================================
--- /trunk/wafp.rb Mon Dec 28 15:55:04 2009
+++ /trunk/wafp.rb Sat Jan 23 13:51:30 2010
@@ -459,7 +459,8 @@
req = nil
rrtry = 0
md5sum = nil
- path = path.to_s.gsub(/^\.\//, '/')
+ path = path.to_s.gsub(/^\.\//, '')
+ path = turi.path !~ /\/$/?("#{turi.path}/"):(turi.path), path
# fetching the static files...
begin
Timeout::timeout(tout) do