Remove packetfu and pcaprub libaries
These should be handled by bundler's Gemfile.unstable
parent
31faf65271
commit
7dafcc76df
|
@ -1,459 +0,0 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, 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 library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete 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 distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
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 Library or any portion
|
||||
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
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 Library, 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 Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you 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.
|
||||
|
||||
If distribution of 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 satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be 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.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library 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.
|
||||
|
||||
9. 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 Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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 with
|
||||
this License.
|
||||
|
||||
11. 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 Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library 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 Library.
|
||||
|
||||
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.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library 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.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser 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 Library
|
||||
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 Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. 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 LIBRARY 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
|
||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
This goal of this project is to provide a consistent interface to LBL's libpcap
|
||||
packet capture library. This project was created because the currently
|
||||
available ruby-pcap library is poorly designed and has been unmaintained since
|
||||
2000. This does not provide packet processing functionality, it simply provides
|
||||
the interface for capturing packets. For packet processing capability, see the
|
||||
PacketRub project (http://packetrub.rubyforge.org).
|
||||
|
||||
Requirements:
|
||||
libpcap - http://www.tcpdump.org
|
||||
|
||||
Build & Install:
|
||||
ruby extconf.rb && make && make install
|
||||
|
||||
The latest version can be obtained from Subversion:
|
||||
svn checkout http://pcaprub.rubyforge.org/svn/trunk/
|
||||
|
||||
The Metasploit Project also provides a Subversion repository:
|
||||
svn checkout http://metasploit.com/svn/framework3/trunk/external/pcaprub/
|
||||
|
||||
The Metasploit Project also added some code from the python netifaces c extension
|
||||
|
||||
Original c/python netifaces code is under MIT-style license.
|
||||
Here goes:
|
||||
|
||||
Copyright (c) 2007, 2008 Alastair Houghton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,117 +0,0 @@
|
|||
require 'mkmf'
|
||||
|
||||
puts("platform is #{RUBY_PLATFORM}")
|
||||
|
||||
########################
|
||||
# Netifaces
|
||||
########################
|
||||
puts "\n[*] Running checks for netifaces code added by metasploit project"
|
||||
puts "-----------------------------------------------------------------"
|
||||
#uncoment to force ioctl on non windows systems
|
||||
#@force_ioctl = true
|
||||
@supported_archs = [ "i386-mingw32", "i486-linux", "x86_64-linux",
|
||||
"universal-darwin10.0", "i386-openbsd4.8", "i386-freebsd8",
|
||||
"arm-linux-eabi" ]
|
||||
#arm-linux-eabi tested on maemo5 / N900
|
||||
puts "[*] Warning : this platform as not been tested" unless @supported_archs.include? RUBY_PLATFORM
|
||||
|
||||
if RUBY_PLATFORM =~ /i386-mingw32/
|
||||
unless have_library("ws2_32" ) and
|
||||
have_library("iphlpapi") and
|
||||
have_header("windows.h") and
|
||||
have_header("winsock2.h") and
|
||||
have_header("iphlpapi.h")
|
||||
puts "\nNot all dependencies are satisfied, please check logs"
|
||||
exit
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
headers = ['net/if_dl.h', 'netash/ash.h','netatalk/at.h', 'netax25/ax25.h',
|
||||
'neteconet/ec.h', 'netipx/ipx.h','netpacket/packet.h', 'netrose/rose.h']
|
||||
if RUBY_PLATFORM =~ /linux/
|
||||
headers += [ 'linux/irda.h', 'linux/atm.h',
|
||||
'linux/llc.h', 'linux/tipc.h',
|
||||
'linux/dn.h']
|
||||
end
|
||||
additionnal_headers = ["sys/types.h","sys/socket.h","sys/un.h","net/if.h","netinet/in.h"]
|
||||
optional_headers = []
|
||||
sockaddrs = [ 'at', 'ax25', 'dl', 'eon', 'in', 'in6',
|
||||
'inarp', 'ipx', 'iso', 'ns', 'un', 'x25',
|
||||
'rose', 'ash', 'ec', 'll', 'atmpvc', 'atmsvc',
|
||||
'dn', 'irda', 'llc']
|
||||
|
||||
# 1) Check for getifaddrs
|
||||
unless @force_ioctl
|
||||
need_ioctl = !(have_func("getifaddrs"))
|
||||
end
|
||||
|
||||
# 2) Check for getnameinfo or redefine it in netifaces.c
|
||||
have_func("getnameinfo")
|
||||
|
||||
# 3) Whitout getifaddrs we'll have to deal with ioctls
|
||||
if need_ioctl or @force_ioctl
|
||||
ioctls = [
|
||||
'SIOCGIFCONF','SIOCGSIZIFCONF','SIOCGIFHWADDR','SIOCGIFADDR','SIOCGIFFLAGS','SIOCGIFDSTADDR',
|
||||
'SIOCGIFBRDADDR','SIOCGIFNETMASK','SIOCGLIFNUM','SIOCGLIFCONF','SIOCGLIFFLAGS']
|
||||
ioctls_headers = ['sys/types.h','sys/socket.h','sys/ioctl.h','net/if.h','netinet/in.h','arpa/inet.h']
|
||||
#TODO Test this on sunos
|
||||
#if RUBY_PLATFORM =~ /sunos/
|
||||
# ioctls_headers += ['unistd.h','stropts.h','sys/sockio.h']
|
||||
#end
|
||||
$defs.push '-DHAVE_SOCKET_IOCTLS'
|
||||
ioctls.each do |ioctl|
|
||||
if have_macro(ioctl, ioctls_headers)
|
||||
$defs.push "-DHAVE_#{ioctl}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# 4) Check for optionnal headers
|
||||
headers.each do |header|
|
||||
if have_header(header)
|
||||
optional_headers.push(header)
|
||||
end
|
||||
end
|
||||
|
||||
# 5) On certain platforms (Linux), there's no sa_len.
|
||||
# Unfortunately, getifaddrs() doesn't return the
|
||||
# lengths, because they're in the sa_len field on just about
|
||||
# everything but Linux.
|
||||
# In this case we will define a macro that will return the sa_len from
|
||||
# the sockaddr_xx structure if they are available
|
||||
if (!have_struct_member("struct sockaddr", "sa_len", ["sys/types.h","sys/socket.h","net/if.h"]))
|
||||
sockaddrs.each do |sockaddr|
|
||||
have_type("struct sockaddr_" + sockaddr, additionnal_headers + optional_headers)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#rework the defs to make them compatible with the original netifaces.c code
|
||||
$defs = $defs.map do |a|
|
||||
if a =~ /^-DHAVE_TYPE_STRUCT_SOCKADDR_.*$/ then a.gsub("TYPE_STRUCT_","")
|
||||
elsif a == "-DHAVE_ST_SA_LEN" then a.gsub("HAVE_ST_","HAVE_SOCKADDR_")
|
||||
else a
|
||||
end
|
||||
end
|
||||
|
||||
########################
|
||||
# Pcap
|
||||
########################
|
||||
|
||||
puts "\n[*] Running checks for pcap code..."
|
||||
puts "-----------------------------------"
|
||||
if /i386-mingw32/ =~ RUBY_PLATFORM
|
||||
dir_config("pcap","C:/WpdPack/include","C:/WpdPack/lib")
|
||||
have_library("wpcap", "pcap_open_live")
|
||||
have_library("wpcap", "pcap_setnonblock")
|
||||
else
|
||||
have_library("pcap", "pcap_open_live")
|
||||
have_library("pcap", "pcap_setnonblock")
|
||||
end
|
||||
|
||||
if ( RUBY_VERSION =~ /^1\.9/ )
|
||||
$CFLAGS += " -DRUBY_19"
|
||||
end
|
||||
|
||||
create_makefile("pcaprub")
|
|
@ -1,816 +0,0 @@
|
|||
#include "ruby.h"
|
||||
|
||||
#ifndef RUBY_19
|
||||
#include "rubysig.h"
|
||||
#endif
|
||||
|
||||
#include "netifaces.h"
|
||||
|
||||
#if !defined(WIN32)
|
||||
#if !HAVE_GETNAMEINFO
|
||||
#undef getnameinfo
|
||||
#undef NI_NUMERICHOST
|
||||
|
||||
#define getnameinfo our_getnameinfo
|
||||
#define NI_NUMERICHOST 1
|
||||
|
||||
/* A very simple getnameinfo() for platforms without */
|
||||
static int
|
||||
getnameinfo (const struct sockaddr *addr, int addr_len,
|
||||
char *buffer, int buflen,
|
||||
char *buf2, int buf2len,
|
||||
int flags)
|
||||
{
|
||||
switch (addr->sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
const struct sockaddr_in *sin = (struct sockaddr_in *)addr;
|
||||
const unsigned char *bytes = (unsigned char *)&sin->sin_addr.s_addr;
|
||||
char tmpbuf[20];
|
||||
|
||||
sprintf (tmpbuf, "%d.%d.%d.%d",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3]);
|
||||
|
||||
strncpy (buffer, tmpbuf, buflen);
|
||||
}
|
||||
break;
|
||||
#ifdef AF_INET6
|
||||
case AF_INET6:
|
||||
{
|
||||
const struct sockaddr_in6 *sin = (const struct sockaddr_in6 *)addr;
|
||||
const unsigned char *bytes = sin->sin6_addr.s6_addr;
|
||||
int n;
|
||||
char tmpbuf[80], *ptr = tmpbuf;
|
||||
int done_double_colon = FALSE;
|
||||
int colon_mode = FALSE;
|
||||
|
||||
for (n = 0; n < 8; ++n)
|
||||
{
|
||||
unsigned char b1 = bytes[2 * n];
|
||||
unsigned char b2 = bytes[2 * n + 1];
|
||||
|
||||
if (b1)
|
||||
{
|
||||
if (colon_mode)
|
||||
{
|
||||
colon_mode = FALSE;
|
||||
*ptr++ = ':';
|
||||
}
|
||||
sprintf (ptr, "%x%02x", b1, b2);
|
||||
ptr += strlen (ptr);
|
||||
*ptr++ = ':';
|
||||
}
|
||||
else if (b2)
|
||||
{
|
||||
if (colon_mode)
|
||||
{
|
||||
colon_mode = FALSE;
|
||||
*ptr++ = ':';
|
||||
}
|
||||
sprintf (ptr, "%x", b2);
|
||||
ptr += strlen (ptr);
|
||||
*ptr++ = ':';
|
||||
}
|
||||
else {
|
||||
if (!colon_mode)
|
||||
{
|
||||
if (done_double_colon)
|
||||
{
|
||||
*ptr++ = '0';
|
||||
*ptr++ = ':';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n == 0)
|
||||
*ptr++ = ':';
|
||||
colon_mode = TRUE;
|
||||
done_double_colon = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (colon_mode)
|
||||
{
|
||||
colon_mode = FALSE;
|
||||
*ptr++ = ':';
|
||||
*ptr++ = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
*--ptr = '\0';
|
||||
}
|
||||
|
||||
strncpy (buffer, tmpbuf, buflen);
|
||||
}
|
||||
break;
|
||||
#endif /* AF_INET6 */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
string_from_sockaddr (struct sockaddr *addr,
|
||||
char *buffer,
|
||||
int buflen)
|
||||
{
|
||||
if (!addr || addr->sa_family == AF_UNSPEC)
|
||||
return -1;
|
||||
|
||||
if (getnameinfo (addr, SA_LEN(addr),
|
||||
buffer, buflen,
|
||||
NULL, 0,
|
||||
NI_NUMERICHOST) != 0)
|
||||
{
|
||||
int n, len;
|
||||
char *ptr;
|
||||
const char *data;
|
||||
|
||||
len = SA_LEN(addr);
|
||||
|
||||
#if HAVE_AF_LINK
|
||||
/* BSD-like systems have AF_LINK */
|
||||
if (addr->sa_family == AF_LINK)
|
||||
{
|
||||
struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr;
|
||||
len = dladdr->sdl_alen;
|
||||
if(len >=0)
|
||||
data = LLADDR(dladdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
#if defined(AF_PACKET)
|
||||
/* Linux has AF_PACKET instead */
|
||||
if (addr->sa_family == AF_PACKET)
|
||||
{
|
||||
struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr;
|
||||
len = lladdr->sll_halen;
|
||||
//amaloteaux: openbsd and maybe other systems have a len of 0 for enc0,pflog0 .. interfaces
|
||||
if(len >=0)
|
||||
data = (const char *)lladdr->sll_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
/* We don't know anything about this sockaddr, so just display
|
||||
the entire data area in binary. */
|
||||
len -= (sizeof (struct sockaddr) - sizeof (addr->sa_data));
|
||||
data = addr->sa_data;
|
||||
#if defined(AF_PACKET)
|
||||
}
|
||||
#endif
|
||||
#if HAVE_AF_LINK
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((buflen < 3 * len) || len <= 0)
|
||||
return -1;
|
||||
|
||||
ptr = buffer;
|
||||
buffer[0] = '\0';
|
||||
|
||||
for (n = 0; n < len; ++n)
|
||||
{
|
||||
sprintf (ptr, "%02x:", data[n] & 0xff);
|
||||
ptr += 3;
|
||||
}
|
||||
*--ptr = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* !defined(WIN32) */
|
||||
|
||||
static VALUE add_to_family(VALUE result, VALUE family, VALUE value)
|
||||
{
|
||||
Check_Type(result, T_HASH);
|
||||
Check_Type(family, T_FIXNUM);
|
||||
Check_Type(value, T_HASH);
|
||||
VALUE list;
|
||||
|
||||
list = rb_hash_aref(result, family);
|
||||
|
||||
if (list == Qnil)
|
||||
list = rb_ary_new();
|
||||
else
|
||||
Check_Type(list, T_ARRAY);
|
||||
|
||||
rb_ary_push(list, value);
|
||||
rb_hash_aset(result, family, list);
|
||||
return result;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rbnetifaces_s_addresses (VALUE class, VALUE dev)
|
||||
{
|
||||
Check_Type(dev, T_STRING);
|
||||
|
||||
VALUE result;
|
||||
int found = FALSE;
|
||||
result = rb_hash_new();
|
||||
|
||||
#if defined(WIN32)
|
||||
PIP_ADAPTER_INFO pAdapterInfo = NULL;
|
||||
PIP_ADAPTER_INFO pInfo = NULL;
|
||||
ULONG ulBufferLength = 0;
|
||||
DWORD dwRet;
|
||||
PIP_ADDR_STRING str;
|
||||
|
||||
//First, retrieve the adapter information. We do this in a loop, in
|
||||
//case someone adds or removes adapters in the meantime.
|
||||
do
|
||||
{
|
||||
dwRet = GetAdaptersInfo(pAdapterInfo, &ulBufferLength);
|
||||
|
||||
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
if (pAdapterInfo)
|
||||
free (pAdapterInfo);
|
||||
pAdapterInfo = (PIP_ADAPTER_INFO)malloc (ulBufferLength);
|
||||
|
||||
if (!pAdapterInfo)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
} while (dwRet == ERROR_BUFFER_OVERFLOW);
|
||||
|
||||
// If we failed, then fail in Ruby too
|
||||
if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA)
|
||||
{
|
||||
if (pAdapterInfo)
|
||||
free (pAdapterInfo);
|
||||
rb_raise(rb_eRuntimeError, "Unable to obtain adapter information.");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
for (pInfo = pAdapterInfo; pInfo; pInfo = pInfo->Next)
|
||||
{
|
||||
char buffer[256];
|
||||
//dev is the iface GUID on windows with "\\Device\\NPF_" prefix
|
||||
int cmpAdapterNamelen = (MAX_ADAPTER_NAME_LENGTH + 4) + 12;
|
||||
char cmpAdapterName[cmpAdapterNamelen];
|
||||
memset(cmpAdapterName, 0x00, cmpAdapterNamelen);
|
||||
strncpy(cmpAdapterName, "\\Device\\NPF_", 12);
|
||||
int AdapterName_len = strlen(pInfo->AdapterName);
|
||||
strncpy(cmpAdapterName + 12, pInfo->AdapterName, AdapterName_len);
|
||||
if (strcmp (cmpAdapterName, StringValuePtr(dev)) != 0)
|
||||
continue;
|
||||
|
||||
VALUE rbhardw = Qnil;
|
||||
VALUE rbaddr = Qnil;
|
||||
VALUE rbnetmask = Qnil;
|
||||
VALUE rbbraddr = Qnil;
|
||||
|
||||
found = TRUE;
|
||||
|
||||
// Do the physical address
|
||||
if (256 >= 3 * pInfo->AddressLength)
|
||||
{
|
||||
VALUE hash_hardw;
|
||||
hash_hardw = rb_hash_new();
|
||||
|
||||
char *ptr = buffer;
|
||||
unsigned n;
|
||||
|
||||
*ptr = '\0';
|
||||
for (n = 0; n < pInfo->AddressLength; ++n)
|
||||
{
|
||||
sprintf (ptr, "%02x:", pInfo->Address[n] & 0xff);
|
||||
ptr += 3;
|
||||
}
|
||||
*--ptr = '\0';
|
||||
|
||||
rbhardw = rb_str_new2(buffer);
|
||||
rb_hash_aset(hash_hardw, rb_str_new2("addr"), rbhardw);
|
||||
result = add_to_family(result, INT2FIX(AF_LINK), hash_hardw);
|
||||
}
|
||||
|
||||
for (str = &pInfo->IpAddressList; str; str = str->Next)
|
||||
{
|
||||
|
||||
VALUE result2;
|
||||
result2 = rb_hash_new();
|
||||
|
||||
if(str->IpAddress.String)
|
||||
rbaddr = rb_str_new2(str->IpAddress.String);
|
||||
if(str->IpMask.String)
|
||||
rbnetmask = rb_str_new2(str->IpMask.String);
|
||||
|
||||
//If this isn't the loopback interface, work out the broadcast
|
||||
//address, for better compatibility with other platforms.
|
||||
if (pInfo->Type != MIB_IF_TYPE_LOOPBACK)
|
||||
{
|
||||
unsigned long inaddr = inet_addr (str->IpAddress.String);
|
||||
unsigned long inmask = inet_addr (str->IpMask.String);
|
||||
struct in_addr in;
|
||||
char *brstr;
|
||||
|
||||
in.S_un.S_addr = (inaddr | ~inmask) & 0xfffffffful;
|
||||
|
||||
brstr = inet_ntoa (in);
|
||||
|
||||
if (brstr)
|
||||
rbbraddr = rb_str_new2(brstr);
|
||||
}
|
||||
|
||||
if (rbaddr)
|
||||
rb_hash_aset(result2, rb_str_new2("addr"), rbaddr);
|
||||
if (rbnetmask)
|
||||
rb_hash_aset(result2, rb_str_new2("netmask"), rbnetmask);
|
||||
if (rbbraddr)
|
||||
rb_hash_aset(result2, rb_str_new2("broadcast"), rbbraddr);
|
||||
|
||||
result = add_to_family(result, INT2FIX(AF_INET), result2);
|
||||
|
||||
}
|
||||
} // for
|
||||
|
||||
free (pAdapterInfo);
|
||||
|
||||
#elif HAVE_GETIFADDRS
|
||||
struct ifaddrs *addrs = NULL;
|
||||
struct ifaddrs *addr = NULL;
|
||||
|
||||
if (getifaddrs (&addrs) < 0)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
}
|
||||
|
||||
for (addr = addrs; addr; addr = addr->ifa_next)
|
||||
{
|
||||
char buffer[256];
|
||||
VALUE rbaddr = Qnil;
|
||||
VALUE rbnetmask = Qnil;
|
||||
VALUE rbbraddr = Qnil;
|
||||
|
||||
if (strcmp (addr->ifa_name, StringValuePtr(dev)) != 0)
|
||||
continue;
|
||||
|
||||
/* Sometimes there are records without addresses (e.g. in the case of a
|
||||
dial-up connection via ppp, which on Linux can have a link address
|
||||
record with no actual address). We skip these as they aren't useful.
|
||||
Thanks to Christian Kauhaus for reporting this issue. */
|
||||
if (!addr->ifa_addr)
|
||||
continue;
|
||||
|
||||
found = TRUE;
|
||||
|
||||
if (string_from_sockaddr (addr->ifa_addr, buffer, sizeof (buffer)) == 0)
|
||||
rbaddr = rb_str_new2(buffer);
|
||||
|
||||
if (string_from_sockaddr (addr->ifa_netmask, buffer, sizeof (buffer)) == 0)
|
||||
rbnetmask = rb_str_new2(buffer);
|
||||
|
||||
if (string_from_sockaddr (addr->ifa_broadaddr, buffer, sizeof (buffer)) == 0)
|
||||
rbbraddr = rb_str_new2(buffer);
|
||||
|
||||
VALUE result2;
|
||||
result2 = rb_hash_new();
|
||||
|
||||
if (rbaddr)
|
||||
rb_hash_aset(result2, rb_str_new2("addr"), rbaddr);
|
||||
if (rbnetmask)
|
||||
rb_hash_aset(result2, rb_str_new2("netmask"), rbnetmask);
|
||||
if (rbbraddr)
|
||||
{
|
||||
if (addr->ifa_flags & (IFF_POINTOPOINT | IFF_LOOPBACK))
|
||||
rb_hash_aset(result2, rb_str_new2("peer"), rbbraddr);
|
||||
else
|
||||
rb_hash_aset(result2, rb_str_new2("broadcast"), rbbraddr);
|
||||
}
|
||||
if (rbaddr || rbnetmask || rbbraddr)
|
||||
result = add_to_family(result, INT2FIX(addr->ifa_addr->sa_family), result2);
|
||||
}
|
||||
freeifaddrs (addrs);
|
||||
#elif HAVE_SOCKET_IOCTLS
|
||||
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (sock < 0)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
struct CNAME(ifreq) ifr;
|
||||
|
||||
char buffer[256];
|
||||
int is_p2p = FALSE;
|
||||
VALUE rbaddr = Qnil;
|
||||
VALUE rbnetmask = Qnil;
|
||||
VALUE rbbraddr = Qnil;
|
||||
VALUE rbdstaddr = Qnil;
|
||||
|
||||
strncpy (ifr.CNAME(ifr_name), StringValuePtr(dev), IFNAMSIZ);
|
||||
|
||||
#if HAVE_SIOCGIFHWADDR
|
||||
if (ioctl (sock, SIOCGIFHWADDR, &ifr) == 0)
|
||||
{
|
||||
if (string_from_sockaddr (&(ifr.CNAME(ifr_addr)), buffer, sizeof (buffer)) == 0)
|
||||
{
|
||||
found = TRUE;
|
||||
|
||||
VALUE rbhardw = Qnil;
|
||||
VALUE hash_hardw;
|
||||
hash_hardw = rb_hash_new();
|
||||
rbhardw = rb_str_new2(buffer);
|
||||
rb_hash_aset(hash_hardw, rb_str_new2("addr"), rbhardw);
|
||||
result = add_to_family(result, INT2FIX(AF_LINK), hash_hardw);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if HAVE_SIOCGIFADDR
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
if (ioctl (sock, SIOCGLIFADDR, &ifr) == 0)
|
||||
{
|
||||
#else
|
||||
if (ioctl (sock, SIOCGIFADDR, &ifr) == 0)
|
||||
{
|
||||
#endif
|
||||
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
||||
{
|
||||
found = TRUE;
|
||||
rbaddr = rb_str_new2(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_SIOCGIFNETMASK
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
if (ioctl (sock, SIOCGLIFNETMASK, &ifr) == 0)
|
||||
{
|
||||
#else
|
||||
if (ioctl (sock, SIOCGIFNETMASK, &ifr) == 0)
|
||||
{
|
||||
#endif
|
||||
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
||||
{
|
||||
found = TRUE;
|
||||
rbnetmask = rb_str_new2(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_SIOCGIFFLAGS
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
if (ioctl (sock, SIOCGLIFFLAGS, &ifr) == 0)
|
||||
{
|
||||
#else
|
||||
if (ioctl (sock, SIOCGIFFLAGS, &ifr) == 0)
|
||||
{
|
||||
#endif
|
||||
|
||||
if (ifr.CNAME(ifr_flags) & IFF_POINTOPOINT)
|
||||
{
|
||||
is_p2p = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_SIOCGIFBRDADDR
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
if (!is_p2p && ioctl (sock, SIOCGLIFBRDADDR, &ifr) == 0)
|
||||
{
|
||||
#else
|
||||
if (!is_p2p && ioctl (sock, SIOCGIFBRDADDR, &ifr) == 0)
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
||||
{
|
||||
found = TRUE;
|
||||
rbbraddr = rb_str_new2(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_SIOCGIFDSTADDR
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
if (is_p2p && ioctl (sock, SIOCGLIFBRDADDR, &ifr) == 0)
|
||||
{
|
||||
#else
|
||||
if (is_p2p && ioctl (sock, SIOCGIFBRDADDR, &ifr) == 0)
|
||||
{
|
||||
#endif
|
||||
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
||||
{
|
||||
found = TRUE;
|
||||
rbdstaddr = rb_str_new2(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
VALUE result2;
|
||||
result2 = rb_hash_new();
|
||||
|
||||
if (rbaddr)
|
||||
rb_hash_aset(result2, rb_str_new2("addr"), rbaddr);
|
||||
if (rbnetmask)
|
||||
rb_hash_aset(result2, rb_str_new2("netmask"), rbnetmask);
|
||||
if (rbbraddr)
|
||||
rb_hash_aset(result2, rb_str_new2("broadcast"), rbbraddr);
|
||||
if (rbdstaddr)
|
||||
rb_hash_aset(result2, rb_str_new2("peer"), rbbraddr);
|
||||
|
||||
if (rbaddr || rbnetmask || rbbraddr || rbdstaddr)
|
||||
result = add_to_family(result, INT2FIX(AF_INET), result2);
|
||||
|
||||
close (sock);
|
||||
#endif /* HAVE_SOCKET_IOCTLS */
|
||||
|
||||
if (found)
|
||||
return result;
|
||||
else
|
||||
return Qnil;
|
||||
|
||||
}
|
||||
|
||||
VALUE
|
||||
rbnetifaces_s_interfaces (VALUE self)
|
||||
{
|
||||
VALUE result;
|
||||
result = rb_ary_new();
|
||||
|
||||
#if defined(WIN32)
|
||||
PIP_ADAPTER_INFO pAdapterInfo = NULL;
|
||||
PIP_ADAPTER_INFO pInfo = NULL;
|
||||
ULONG ulBufferLength = 0;
|
||||
DWORD dwRet;
|
||||
|
||||
// First, retrieve the adapter information
|
||||
do {
|
||||
dwRet = GetAdaptersInfo(pAdapterInfo, &ulBufferLength);
|
||||
|
||||
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
if (pAdapterInfo)
|
||||
free (pAdapterInfo);
|
||||
pAdapterInfo = (PIP_ADAPTER_INFO)malloc (ulBufferLength);
|
||||
|
||||
if (!pAdapterInfo)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
}
|
||||
}
|
||||
} while (dwRet == ERROR_BUFFER_OVERFLOW);
|
||||
|
||||
// If we failed, then fail in Ruby too
|
||||
if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA)
|
||||
{
|
||||
if (pAdapterInfo)
|
||||
free (pAdapterInfo);
|
||||
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
return Qnil;
|
||||
}
|
||||
if (dwRet == ERROR_NO_DATA)
|
||||
{
|
||||
free (pAdapterInfo);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (pInfo = pAdapterInfo; pInfo; pInfo = pInfo->Next)
|
||||
{
|
||||
int outputnamelen = (MAX_ADAPTER_NAME_LENGTH + 4) + 12;
|
||||
char outputname[outputnamelen];
|
||||
memset(outputname, 0x00, outputnamelen);
|
||||
strncpy(outputname, "\\Device\\NPF_", 12);
|
||||
int AdapterName_len = strlen(pInfo->AdapterName);
|
||||
strncpy(outputname + 12, pInfo->AdapterName, AdapterName_len);
|
||||
VALUE ifname = rb_str_new2(outputname) ;
|
||||
|
||||
if(!rb_ary_includes(result, ifname))
|
||||
rb_ary_push(result, ifname);
|
||||
}
|
||||
|
||||
free (pAdapterInfo);
|
||||
|
||||
#elif HAVE_GETIFADDRS
|
||||
const char *prev_name = NULL;
|
||||
struct ifaddrs *addrs = NULL;
|
||||
struct ifaddrs *addr = NULL;
|
||||
|
||||
if (getifaddrs (&addrs) < 0)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
}
|
||||
|
||||
for (addr = addrs; addr; addr = addr->ifa_next)
|
||||
{
|
||||
if (!prev_name || strncmp (addr->ifa_name, prev_name, IFNAMSIZ) != 0)
|
||||
{
|
||||
VALUE ifname = rb_str_new2(addr->ifa_name);
|
||||
|
||||
if(!rb_ary_includes(result, ifname))
|
||||
rb_ary_push(result, ifname);
|
||||
|
||||
prev_name = addr->ifa_name;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs (addrs);
|
||||
#elif HAVE_SIOCGIFCONF
|
||||
|
||||
const char *prev_name = NULL;
|
||||
int fd = socket (AF_INET, SOCK_DGRAM, 0);
|
||||
struct CNAME(ifconf) ifc;
|
||||
int len = -1, n;
|
||||
if (fd < 0) {
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
// Try to find out how much space we need
|
||||
#if HAVE_SIOCGSIZIFCONF
|
||||
if (ioctl (fd, SIOCGSIZIFCONF, &len) < 0)
|
||||
len = -1;
|
||||
#elif HAVE_SIOCGLIFNUM
|
||||
#error This code need to be checked first
|
||||
/*
|
||||
{ struct lifnum lifn;
|
||||
lifn.lifn_family = AF_UNSPEC;
|
||||
lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
|
||||
ifc.lifc_family = AF_UNSPEC;
|
||||
ifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
|
||||
if (ioctl (fd, SIOCGLIFNUM, (char *)&lifn) < 0)
|
||||
len = -1;
|
||||
else
|
||||
len = lifn.lifn_count;
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
// As a last resort, guess
|
||||
if (len < 0)
|
||||
len = 64;
|
||||
|
||||
ifc.CNAME(ifc_len) = len * sizeof (struct CNAME(ifreq));
|
||||
ifc.CNAME(ifc_buf) = malloc (ifc.CNAME(ifc_len));
|
||||
|
||||
if (!ifc.CNAME(ifc_buf)) {
|
||||
close (fd);
|
||||
rb_raise(rb_eRuntimeError, "Not enough memory");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
if (ioctl (fd, SIOCGLIFCONF, &ifc) < 0) {
|
||||
#else
|
||||
if (ioctl (fd, SIOCGIFCONF, &ifc) < 0) {
|
||||
|
||||
#endif
|
||||
free (ifc.CNAME(ifc_req));
|
||||
close (fd);
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
struct CNAME(ifreq) *pfreq = ifc.CNAME(ifc_req);
|
||||
|
||||
for (n = 0; n < ifc.CNAME(ifc_len)/sizeof(struct CNAME(ifreq));n++,pfreq++)
|
||||
{
|
||||
if (!prev_name || strncmp (prev_name, pfreq->CNAME(ifr_name), IFNAMSIZ) != 0)
|
||||
{
|
||||
VALUE ifname = rb_str_new2(pfreq->CNAME(ifr_name));
|
||||
if(!rb_ary_includes(result, ifname))
|
||||
rb_ary_push(result, ifname);
|
||||
|
||||
prev_name = pfreq->CNAME(ifr_name);
|
||||
}
|
||||
}
|
||||
|
||||
free (ifc.CNAME(ifc_buf));
|
||||
close (fd);
|
||||
|
||||
#endif //
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//This function is usefull only under windows to retrieve some additionnal interfaces informations
|
||||
VALUE
|
||||
rbnetifaces_s_interface_info (VALUE self, VALUE dev)
|
||||
{
|
||||
VALUE result = Qnil;
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
PIP_ADAPTER_INFO pAdapterInfo = NULL;
|
||||
PIP_ADAPTER_INFO pInfo = NULL;
|
||||
ULONG ulBufferLength = 0;
|
||||
DWORD dwRet;
|
||||
|
||||
// First, retrieve the adapter information
|
||||
do {
|
||||
dwRet = GetAdaptersInfo(pAdapterInfo, &ulBufferLength);
|
||||
|
||||
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
if (pAdapterInfo)
|
||||
free (pAdapterInfo);
|
||||
pAdapterInfo = (PIP_ADAPTER_INFO)malloc (ulBufferLength);
|
||||
|
||||
if (!pAdapterInfo)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
}
|
||||
}
|
||||
} while (dwRet == ERROR_BUFFER_OVERFLOW);
|
||||
|
||||
// If we failed, then fail in Ruby too
|
||||
if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA)
|
||||
{
|
||||
if (pAdapterInfo)
|
||||
free (pAdapterInfo);
|
||||
|
||||
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
||||
return Qnil;
|
||||
}
|
||||
if (dwRet == ERROR_NO_DATA)
|
||||
{
|
||||
free (pAdapterInfo);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (pInfo = pAdapterInfo; pInfo; pInfo = pInfo->Next)
|
||||
{
|
||||
|
||||
//dev is the iface GUID on windows with "\\Device\\NPF_" prefix
|
||||
int cmpAdapterNamelen = (MAX_ADAPTER_NAME_LENGTH + 4) + 12;
|
||||
char cmpAdapterName[cmpAdapterNamelen];
|
||||
memset(cmpAdapterName, 0x00, cmpAdapterNamelen);
|
||||
strncpy(cmpAdapterName, "\\Device\\NPF_", 12);
|
||||
int AdapterName_len = strlen(pInfo->AdapterName);
|
||||
strncpy(cmpAdapterName + 12, pInfo->AdapterName, AdapterName_len);
|
||||
if (strcmp (cmpAdapterName, StringValuePtr(dev)) != 0)
|
||||
continue;
|
||||
|
||||
result = rb_hash_new();
|
||||
rb_hash_aset(result, rb_str_new2("description"), rb_str_new2(pInfo->Description));
|
||||
rb_hash_aset(result, rb_str_new2("guid"), rb_str_new2(pInfo->AdapterName));
|
||||
|
||||
// Get the name from the registry
|
||||
const char* prefix = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\";
|
||||
const char* sufix = "\\Connection";
|
||||
int prefix_len = strlen(prefix);
|
||||
int sufix_len = strlen(sufix);
|
||||
int adaptername_len = strlen(pInfo->AdapterName);
|
||||
char* keypath = NULL;
|
||||
keypath = malloc(prefix_len + sufix_len + adaptername_len + 1);
|
||||
memset(keypath, 0x00, prefix_len + sufix_len + adaptername_len + 1);
|
||||
strncpy(keypath, prefix, prefix_len);
|
||||
strncpy(keypath + prefix_len, pInfo->AdapterName, adaptername_len);
|
||||
strncpy(keypath + prefix_len + adaptername_len, sufix, sufix_len);
|
||||
|
||||
HKEY hKey;
|
||||
LONG lRet = 0;
|
||||
LPBYTE buffer = NULL;
|
||||
DWORD dwSize = 0;
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
|
||||
{
|
||||
// obtain current value size
|
||||
lRet = RegQueryValueEx(hKey, "Name", NULL, NULL, NULL, &dwSize);
|
||||
if (dwSize > 0 && ERROR_SUCCESS == lRet)
|
||||
{
|
||||
buffer = malloc((dwSize * sizeof(BYTE)) + 4);
|
||||
memset(buffer, 0x00, (dwSize * sizeof(BYTE)) + 4);
|
||||
lRet = RegQueryValueEx(hKey, "Name", NULL, NULL, buffer, &dwSize);
|
||||
if (ERROR_SUCCESS == lRet)
|
||||
{
|
||||
rb_hash_aset(result, rb_str_new2("name"), rb_str_new2(buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
rb_hash_aset(result, rb_str_new2("name"), rb_str_new2(""));
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
rb_hash_aset(result, rb_str_new2("name"), rb_str_new2(""));
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
rb_hash_aset(result, rb_str_new2("name"), rb_str_new2(""));
|
||||
}
|
||||
free(keypath);
|
||||
}
|
||||
free (pAdapterInfo);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
#ifndef WIN32
|
||||
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <net/if.h>
|
||||
# include <netdb.h>
|
||||
|
||||
# if HAVE_SOCKET_IOCTLS
|
||||
# include <sys/ioctl.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
#if defined(__sun)
|
||||
#include <unistd.h>
|
||||
#include <stropts.h>
|
||||
#include <sys/sockio.h>
|
||||
#endif
|
||||
# endif /* HAVE_SOCKET_IOCTLS */
|
||||
|
||||
/* For logical interfaces support we convert all names to same name prefixed with l */
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
#define CNAME(x) l##x
|
||||
#else
|
||||
#define CNAME(x) x
|
||||
#endif
|
||||
|
||||
#if HAVE_NET_IF_DL_H
|
||||
# include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
/* For Linux, include all the sockaddr
|
||||
definitions we can lay our hands on. */
|
||||
#if !HAVE_SOCKADDR_SA_LEN
|
||||
# if HAVE_NETASH_ASH_H
|
||||
# include <netash/ash.h>
|
||||
# endif
|
||||
# if HAVE_NETATALK_AT_H
|
||||
# include <netatalk/at.h>
|
||||
# endif
|
||||
# if HAVE_NETAX25_AX25_H
|
||||
# include <netax25/ax25.h>
|
||||
# endif
|
||||
# if HAVE_NETECONET_EC_H
|
||||
# include <neteconet/ec.h>
|
||||
# endif
|
||||
# if HAVE_NETIPX_IPX_H
|
||||
# include <netipx/ipx.h>
|
||||
# endif
|
||||
# if HAVE_NETPACKET_PACKET_H
|
||||
# include <netpacket/packet.h>
|
||||
# endif
|
||||
# if HAVE_NETROSE_ROSE_H
|
||||
# include <netrose/rose.h>
|
||||
# endif
|
||||
# if HAVE_LINUX_IRDA_H
|
||||
# include <linux/irda.h>
|
||||
# endif
|
||||
# if HAVE_LINUX_ATM_H
|
||||
# include <linux/atm.h>
|
||||
# endif
|
||||
# if HAVE_LINUX_LLC_H
|
||||
# include <linux/llc.h>
|
||||
# endif
|
||||
# if HAVE_LINUX_TIPC_H
|
||||
# include <linux/tipc.h>
|
||||
# endif
|
||||
# if HAVE_LINUX_DN_H
|
||||
# include <linux/dn.h>
|
||||
# endif
|
||||
|
||||
/* Map address families to sizes of sockaddr structs */
|
||||
static int af_to_len(int af)
|
||||
{
|
||||
switch (af)
|
||||
{
|
||||
case AF_INET: return sizeof (struct sockaddr_in);
|
||||
#if defined(AF_INET6) && HAVE_SOCKADDR_IN6
|
||||
case AF_INET6: return sizeof (struct sockaddr_in6);
|
||||
#endif
|
||||
#if defined(AF_AX25) && HAVE_SOCKADDR_AX25
|
||||
# if defined(AF_NETROM)
|
||||
case AF_NETROM: /* I'm assuming this is carried over x25 */
|
||||
# endif
|
||||
case AF_AX25: return sizeof (struct sockaddr_ax25);
|
||||
#endif
|
||||
#if defined(AF_IPX) && HAVE_SOCKADDR_IPX
|
||||
case AF_IPX: return sizeof (struct sockaddr_ipx);
|
||||
#endif
|
||||
#if defined(AF_APPLETALK) && HAVE_SOCKADDR_AT
|
||||
case AF_APPLETALK: return sizeof (struct sockaddr_at);
|
||||
#endif
|
||||
#if defined(AF_ATMPVC) && HAVE_SOCKADDR_ATMPVC
|
||||
case AF_ATMPVC: return sizeof (struct sockaddr_atmpvc);
|
||||
#endif
|
||||
#if defined(AF_ATMSVC) && HAVE_SOCKADDR_ATMSVC
|
||||
case AF_ATMSVC: return sizeof (struct sockaddr_atmsvc);
|
||||
#endif
|
||||
#if defined(AF_X25) && HAVE_SOCKADDR_X25
|
||||
case AF_X25: return sizeof (struct sockaddr_x25);
|
||||
#endif
|
||||
#if defined(AF_ROSE) && HAVE_SOCKADDR_ROSE
|
||||
case AF_ROSE: return sizeof (struct sockaddr_rose);
|
||||
#endif
|
||||
#if defined(AF_DECnet) && HAVE_SOCKADDR_DN
|
||||
case AF_DECnet: return sizeof (struct sockaddr_dn);
|
||||
#endif
|
||||
#if defined(AF_PACKET) && HAVE_SOCKADDR_LL
|
||||
case AF_PACKET: return sizeof (struct sockaddr_ll);
|
||||
#endif
|
||||
#if defined(AF_ASH) && HAVE_SOCKADDR_ASH
|
||||
case AF_ASH: return sizeof (struct sockaddr_ash);
|
||||
#endif
|
||||
#if defined(AF_ECONET) && HAVE_SOCKADDR_EC
|
||||
case AF_ECONET: return sizeof (struct sockaddr_ec);
|
||||
#endif
|
||||
#if defined(AF_IRDA) && HAVE_SOCKADDR_IRDA
|
||||
case AF_IRDA: return sizeof (struct sockaddr_irda);
|
||||
#endif
|
||||
}
|
||||
return sizeof (struct sockaddr);
|
||||
}
|
||||
|
||||
#define SA_LEN(sa) af_to_len(sa->sa_family)
|
||||
#if HAVE_SIOCGLIFNUM
|
||||
#define SS_LEN(sa) af_to_len(sa->ss_family)
|
||||
#else
|
||||
#define SS_LEN(sa) SA_LEN(sa)
|
||||
#endif
|
||||
#else
|
||||
//remove a warning on openbsd
|
||||
#ifndef SA_LEN
|
||||
#define SA_LEN(sa) sa->sa_len
|
||||
#endif
|
||||
#endif /* !HAVE_SOCKADDR_SA_LEN */
|
||||
|
||||
# if HAVE_GETIFADDRS
|
||||
# include <ifaddrs.h>
|
||||
# endif /* HAVE_GETIFADDRS */
|
||||
|
||||
# if !HAVE_GETIFADDRS && (!HAVE_SOCKET_IOCTLS || !HAVE_SIOCGIFCONF)
|
||||
/* If the platform doesn't define, what we need, barf. If you're seeing this,
|
||||
it means you need to write suitable code to retrieve interface information
|
||||
on your system. */
|
||||
# error You need to add code for your platform.
|
||||
# endif
|
||||
|
||||
#else /* defined(WIN32) */
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
#endif /* defined(WIN32) */
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
/* On systems without AF_LINK (Windows, for instance), define it anyway, but
|
||||
give it a crazy value. On Linux, which has AF_PACKET but not AF_LINK,
|
||||
define AF_LINK as the latter instead. */
|
||||
#ifndef AF_LINK
|
||||
# ifdef AF_PACKET
|
||||
# define AF_LINK AF_PACKET
|
||||
# else
|
||||
# define AF_LINK -1000
|
||||
# endif
|
||||
# define HAVE_AF_LINK 0
|
||||
#else
|
||||
# define HAVE_AF_LINK 1
|
||||
#endif
|
||||
|
||||
|
||||
//Prototypes
|
||||
//Get a list of the adresses for a network interface
|
||||
VALUE rbnetifaces_s_addresses (VALUE class, VALUE dev);
|
||||
//Get a list of the network interfaces
|
||||
VALUE rbnetifaces_s_interfaces (VALUE self);
|
||||
//This function is usefull only under windows to retrieve some additionnal interfaces informations
|
||||
VALUE rbnetifaces_s_interface_info (VALUE self, VALUE dev);
|
||||
|
|
@ -1,783 +0,0 @@
|
|||
#include "ruby.h"
|
||||
|
||||
#ifndef RUBY_19
|
||||
#include "rubysig.h"
|
||||
#endif
|
||||
|
||||
#include "netifaces.h"
|
||||
|
||||
#include <pcap.h>
|
||||
|
||||
#if !defined(WIN32)
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#if !defined(DLT_IEEE802_11_RADIO_AVS)
|
||||
#define DLT_IEEE802_11_RADIO_AVS 163
|
||||
#endif
|
||||
|
||||
#if !defined(DLT_LINUX_SLL)
|
||||
#define DLT_LINUX_SLL 113
|
||||
#endif
|
||||
|
||||
#if !defined(DLT_PRISM_HEADER)
|
||||
#define DLT_PRISM_HEADER 119
|
||||
#endif
|
||||
|
||||
#if !defined(DLT_AIRONET_HEADER)
|
||||
#define DLT_AIRONET_HEADER 120
|
||||
#endif
|
||||
|
||||
#if !defined(PCAP_NETMASK_UNKNOWN)
|
||||
/*
|
||||
* Value to pass to pcap_compile() as the netmask if you don't know what
|
||||
* the netmask is.
|
||||
*/
|
||||
#define PCAP_NETMASK_UNKNOWN 0xffffffff
|
||||
#endif
|
||||
|
||||
static VALUE rb_cPcap;
|
||||
|
||||
#define PCAPRUB_VERSION "0.9-dev"
|
||||
|
||||
#define OFFLINE 1
|
||||
#define LIVE 2
|
||||
|
||||
typedef struct rbpcap {
|
||||
pcap_t *pd;
|
||||
pcap_dumper_t *pdt;
|
||||
char iface[256];
|
||||
char type;
|
||||
} rbpcap_t;
|
||||
|
||||
|
||||
typedef struct rbpcapjob {
|
||||
struct pcap_pkthdr hdr;
|
||||
unsigned char *pkt;
|
||||
int wtf;
|
||||
} rbpcapjob_t;
|
||||
|
||||
static VALUE
|
||||
rbpcap_s_version(VALUE class)
|
||||
{
|
||||
return rb_str_new2(PCAPRUB_VERSION);
|
||||
}
|
||||
|
||||
|
||||
static VALUE
|
||||
rbpcap_s_lookupdev(VALUE self)
|
||||
{
|
||||
char *dev = NULL;
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
VALUE ret_dev; /* device string to return */
|
||||
#if defined(WIN32) /* pcap_lookupdev is broken on windows */
|
||||
pcap_if_t *alldevs;
|
||||
pcap_if_t *d;
|
||||
|
||||
/* Retrieve the device list from the local machine */
|
||||
if (pcap_findalldevs(&alldevs,eb) == -1) {
|
||||
rb_raise(rb_eRuntimeError,"%s",eb);
|
||||
}
|
||||
|
||||
/* Find the first interface with an address and not loopback */
|
||||
for(d = alldevs; d != NULL; d= d->next) {
|
||||
if(d->name && d->addresses && !(d->flags & PCAP_IF_LOOPBACK)) {
|
||||
dev=d->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev == NULL) {
|
||||
rb_raise(rb_eRuntimeError,"%s","No valid interfaces found, Make sure WinPcap is installed.\n");
|
||||
}
|
||||
ret_dev = rb_str_new2(dev);
|
||||
/* We don't need any more the device list. Free it */
|
||||
pcap_freealldevs(alldevs);
|
||||
#else
|
||||
dev = pcap_lookupdev(eb);
|
||||
if (dev == NULL) {
|
||||
rb_raise(rb_eRuntimeError, "%s", eb);
|
||||
}
|
||||
ret_dev = rb_str_new2(dev);
|
||||
#endif
|
||||
return ret_dev;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_s_lookupaddrs(VALUE self,VALUE dev)
|
||||
{
|
||||
char *ldev = NULL;
|
||||
pcap_addr_t *addresses, *a = NULL;
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
VALUE ret_dev; /* device string to return */
|
||||
pcap_if_t *alldevs;
|
||||
pcap_if_t *d;
|
||||
VALUE list;
|
||||
|
||||
/* Retrieve the device list from the local machine */
|
||||
if (pcap_findalldevs(&alldevs,eb) == -1) {
|
||||
rb_raise(rb_eRuntimeError,"%s",eb);
|
||||
}
|
||||
|
||||
/* Find the first interface with an address and not loopback */
|
||||
for(d = alldevs; d != NULL; d= d->next) {
|
||||
if(strcmp(d->name,StringValuePtr(dev)) == 0 && d->addresses && !(d->flags & PCAP_IF_LOOPBACK)) {
|
||||
ldev=d->name;
|
||||
addresses=d->addresses;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ldev == NULL) {
|
||||
rb_raise(rb_eRuntimeError,"%s","No valid interfaces found.\n");
|
||||
}
|
||||
|
||||
list = rb_ary_new();
|
||||
for(a = addresses; a != NULL; a= a->next) {
|
||||
switch(a->addr->sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
if (a->addr)
|
||||
rb_ary_push(list, rb_str_new2(inet_ntoa((((struct sockaddr_in *)a->addr)->sin_addr))));
|
||||
break;
|
||||
/* Don't like the __MINGW32__ comment for the moment need some testing ...
|
||||
case AF_INET6:
|
||||
#ifndef __MINGW32__ // Cygnus doesn't have IPv6
|
||||
if (a->addr)
|
||||
printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str)));
|
||||
#endif
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pcap_freealldevs(alldevs);
|
||||
return(list);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_s_lookupnet(VALUE self, VALUE dev)
|
||||
{
|
||||
bpf_u_int32 net, mask, m;
|
||||
struct in_addr addr;
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
VALUE list;
|
||||
|
||||
Check_Type(dev, T_STRING);
|
||||
if (pcap_lookupnet(StringValuePtr(dev), &net, &mask, eb) == -1) {
|
||||
rb_raise(rb_eRuntimeError, "%s", eb);
|
||||
}
|
||||
|
||||
addr.s_addr = net;
|
||||
m = ntohl(mask);
|
||||
list = rb_ary_new();
|
||||
rb_ary_push(list, rb_str_new2((char *) inet_ntoa(addr)));
|
||||
rb_ary_push(list, UINT2NUM(m));
|
||||
return(list);
|
||||
}
|
||||
|
||||
|
||||
static int rbpcap_ready(rbpcap_t *rbp) {
|
||||
if(! rbp->pd) {
|
||||
rb_raise(rb_eArgError, "a device or pcap file must be opened first");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rbpcap_free(rbpcap_t *rbp) {
|
||||
if (rbp->pd)
|
||||
pcap_close(rbp->pd);
|
||||
|
||||
if (rbp->pdt)
|
||||
pcap_dump_close(rbp->pdt);
|
||||
|
||||
rbp->pd = NULL;
|
||||
rbp->pdt = NULL;
|
||||
free(rbp);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_new_s(VALUE class)
|
||||
{
|
||||
VALUE self;
|
||||
rbpcap_t *rbp;
|
||||
|
||||
// need to make destructor do a pcap_close later
|
||||
self = Data_Make_Struct(class, rbpcap_t, 0, rbpcap_free, rbp);
|
||||
rb_obj_call_init(self, 0, 0);
|
||||
|
||||
memset(rbp, 0, sizeof(rbpcap_t));
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_setfilter(VALUE self, VALUE filter)
|
||||
{
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
rbpcap_t *rbp;
|
||||
u_int32_t mask = 0, netid = 0;
|
||||
struct bpf_program bpf;
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
if(TYPE(filter) != T_STRING)
|
||||
rb_raise(rb_eArgError, "filter must be a string");
|
||||
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
|
||||
if(rbp->type == LIVE)
|
||||
if(pcap_lookupnet(rbp->iface, &netid, &mask, eb) < 0)
|
||||
rb_raise(rb_eRuntimeError, "%s", eb);
|
||||
|
||||
if(pcap_compile(rbp->pd, &bpf, RSTRING_PTR(filter), 0, mask) < 0)
|
||||
rb_raise(rb_eRuntimeError, "invalid bpf filter");
|
||||
|
||||
if(pcap_setfilter(rbp->pd, &bpf) < 0)
|
||||
rb_raise(rb_eRuntimeError, "unable to set bpf filter");
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
static VALUE
|
||||
rbpcap_open_live(VALUE self, VALUE iface,VALUE snaplen,VALUE promisc, VALUE timeout)
|
||||
{
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
rbpcap_t *rbp;
|
||||
int promisc_value = 0;
|
||||
|
||||
if(TYPE(iface) != T_STRING)
|
||||
rb_raise(rb_eArgError, "interface must be a string");
|
||||
if(TYPE(snaplen) != T_FIXNUM)
|
||||
rb_raise(rb_eArgError, "snaplen must be a fixnum");
|
||||
if(TYPE(timeout) != T_FIXNUM)
|
||||
rb_raise(rb_eArgError, "timeout must be a fixnum");
|
||||
|
||||
switch(promisc) {
|
||||
case Qtrue:
|
||||
promisc_value = 1;
|
||||
break;
|
||||
case Qfalse:
|
||||
promisc_value = 0;
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eTypeError, "Argument not boolean");
|
||||
}
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
|
||||
rbp->type = LIVE;
|
||||
memset(rbp->iface, 0, sizeof(rbp->iface));
|
||||
strncpy(rbp->iface, RSTRING_PTR(iface), sizeof(rbp->iface) - 1);
|
||||
|
||||
|
||||
if(rbp->pd) {
|
||||
pcap_close(rbp->pd);
|
||||
}
|
||||
|
||||
rbp->pd = pcap_open_live(
|
||||
RSTRING_PTR(iface),
|
||||
NUM2INT(snaplen),
|
||||
promisc_value,
|
||||
NUM2INT(timeout),
|
||||
eb
|
||||
);
|
||||
|
||||
if(!rbp->pd)
|
||||
rb_raise(rb_eRuntimeError, "%s", eb);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_open_live_s(VALUE class, VALUE iface, VALUE snaplen, VALUE promisc, VALUE timeout)
|
||||
{
|
||||
VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
|
||||
return rbpcap_open_live(iPcap, iface, snaplen, promisc, timeout);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_open_offline(VALUE self, VALUE filename)
|
||||
{
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
rbpcap_t *rbp;
|
||||
|
||||
if(TYPE(filename) != T_STRING)
|
||||
rb_raise(rb_eArgError, "filename must be a string");
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
memset(rbp->iface, 0, sizeof(rbp->iface));
|
||||
rbp->type = OFFLINE;
|
||||
|
||||
rbp->pd = pcap_open_offline(
|
||||
RSTRING_PTR(filename),
|
||||
eb
|
||||
);
|
||||
|
||||
if(!rbp->pd)
|
||||
rb_raise(rb_eRuntimeError, "%s", eb);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
static VALUE
|
||||
rbpcap_open_offline_s(VALUE class, VALUE filename)
|
||||
{
|
||||
VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
|
||||
|
||||
return rbpcap_open_offline(iPcap, filename);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_open_dead(VALUE self, VALUE linktype, VALUE snaplen)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
|
||||
|
||||
if(TYPE(linktype) != T_FIXNUM)
|
||||
rb_raise(rb_eArgError, "linktype must be a fixnum");
|
||||
if(TYPE(snaplen) != T_FIXNUM)
|
||||
rb_raise(rb_eArgError, "snaplen must be a fixnum");
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
memset(rbp->iface, 0, sizeof(rbp->iface));
|
||||
rbp->type = OFFLINE;
|
||||
|
||||
rbp->pd = pcap_open_dead(
|
||||
NUM2INT(linktype),
|
||||
NUM2INT(snaplen)
|
||||
);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_open_dead_s(VALUE class, VALUE linktype, VALUE snaplen)
|
||||
{
|
||||
VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
|
||||
|
||||
return rbpcap_open_dead(iPcap, linktype, snaplen);
|
||||
}
|
||||
|
||||
|
||||
static VALUE
|
||||
rbpcap_dump_open(VALUE self, VALUE filename)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
|
||||
if(TYPE(filename) != T_STRING)
|
||||
rb_raise(rb_eArgError, "filename must be a string");
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
rbp->pdt = pcap_dump_open(
|
||||
rbp->pd,
|
||||
RSTRING_PTR(filename)
|
||||
);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//not sure if this deviates too much from the way the rest of this class works?
|
||||
static VALUE
|
||||
rbpcap_dump(VALUE self, VALUE caplen, VALUE pktlen, VALUE packet)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
struct pcap_pkthdr pcap_hdr;
|
||||
|
||||
if(TYPE(packet) != T_STRING)
|
||||
rb_raise(rb_eArgError, "packet data must be a string");
|
||||
if(TYPE(caplen) != T_FIXNUM)
|
||||
rb_raise(rb_eArgError, "caplen must be a fixnum");
|
||||
if(TYPE(pktlen) != T_FIXNUM)
|
||||
rb_raise(rb_eArgError, "pktlen must be a fixnum");
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
gettimeofday(&pcap_hdr.ts, NULL);
|
||||
pcap_hdr.caplen = NUM2UINT(caplen);
|
||||
pcap_hdr.len = NUM2UINT(pktlen);
|
||||
|
||||
pcap_dump(
|
||||
(u_char*)rbp->pdt,
|
||||
&pcap_hdr,
|
||||
(unsigned char *)RSTRING_PTR(packet)
|
||||
);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_inject(VALUE self, VALUE payload)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
|
||||
if(TYPE(payload) != T_STRING)
|
||||
rb_raise(rb_eArgError, "payload must be a string");
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
#if defined(WIN32)
|
||||
/* WinPcap does not have a pcap_inject call we use pcap_sendpacket, if it suceedes
|
||||
* we simply return the amount of packets request to inject, else we fail.
|
||||
*/
|
||||
if(pcap_sendpacket(rbp->pd, RSTRING_PTR(payload), RSTRING_LEN(payload)) != 0) {
|
||||
rb_raise(rb_eRuntimeError, "%s", pcap_geterr(rbp->pd));
|
||||
}
|
||||
return INT2NUM(RSTRING_LEN(payload));
|
||||
#else
|
||||
return INT2NUM(pcap_inject(rbp->pd, RSTRING_PTR(payload), RSTRING_LEN(payload)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void rbpcap_handler(rbpcapjob_t *job, struct pcap_pkthdr *hdr, u_char *pkt){
|
||||
job->pkt = (unsigned char *)pkt;
|
||||
job->hdr = *hdr;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_next(VALUE self)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
rbpcapjob_t job;
|
||||
char eb[PCAP_ERRBUF_SIZE];
|
||||
int ret;
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
pcap_setnonblock(rbp->pd, 1, eb);
|
||||
|
||||
#ifndef RUBY_19
|
||||
TRAP_BEG;
|
||||
#endif
|
||||
ret = pcap_dispatch(rbp->pd, 1, (pcap_handler) rbpcap_handler, (u_char *)&job);
|
||||
#ifndef RUBY_19
|
||||
TRAP_END;
|
||||
#endif
|
||||
|
||||
if(rbp->type == OFFLINE && ret <= 0) return Qnil;
|
||||
|
||||
if(ret > 0 && job.hdr.caplen > 0)
|
||||
return rb_str_new((char *) job.pkt, job.hdr.caplen);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_capture(VALUE self)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
int fno = -1;
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
|
||||
#if !defined(WIN32)
|
||||
fno = pcap_get_selectable_fd(rbp->pd);
|
||||
#else
|
||||
fno = pcap_fileno(rbp->pd);
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
VALUE packet = rbpcap_next(self);
|
||||
if(packet == Qnil && rbp->type == OFFLINE) break;
|
||||
packet == Qnil ? rb_thread_wait_fd(fno) : rb_yield(packet);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
static VALUE
|
||||
rbpcap_datalink(VALUE self)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
|
||||
return INT2NUM(pcap_datalink(rbp->pd));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_snapshot(VALUE self)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
|
||||
return INT2NUM(pcap_snapshot(rbp->pd));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rbpcap_stats(VALUE self)
|
||||
{
|
||||
rbpcap_t *rbp;
|
||||
struct pcap_stat stat;
|
||||
VALUE hash;
|
||||
|
||||
Data_Get_Struct(self, rbpcap_t, rbp);
|
||||
|
||||
if(! rbpcap_ready(rbp)) return self;
|
||||
|
||||
if (pcap_stats(rbp->pd, &stat) == -1)
|
||||
return Qnil;
|
||||
|
||||
hash = rb_hash_new();
|
||||
rb_hash_aset(hash, rb_str_new2("recv"), UINT2NUM(stat.ps_recv));
|
||||
rb_hash_aset(hash, rb_str_new2("drop"), UINT2NUM(stat.ps_drop));
|
||||
rb_hash_aset(hash, rb_str_new2("idrop"), UINT2NUM(stat.ps_ifdrop));
|
||||
return hash;
|
||||
}
|
||||
|
||||
void
|
||||
Init_pcaprub()
|
||||
{
|
||||
// Pcap
|
||||
rb_cPcap = rb_define_class("Pcap", rb_cObject);
|
||||
rb_define_module_function(rb_cPcap, "version", rbpcap_s_version, 0);
|
||||
rb_define_module_function(rb_cPcap, "lookupdev", rbpcap_s_lookupdev, 0);
|
||||
rb_define_module_function(rb_cPcap, "lookupnet", rbpcap_s_lookupnet, 1);
|
||||
rb_define_module_function(rb_cPcap, "lookupaddrs", rbpcap_s_lookupaddrs, 1);
|
||||
|
||||
rb_define_const(rb_cPcap, "DLT_NULL", INT2NUM(DLT_NULL));
|
||||
rb_define_const(rb_cPcap, "DLT_EN10MB", INT2NUM(DLT_EN10MB));
|
||||
rb_define_const(rb_cPcap, "DLT_EN3MB", INT2NUM(DLT_EN3MB));
|
||||
rb_define_const(rb_cPcap, "DLT_AX25", INT2NUM(DLT_AX25));
|
||||
rb_define_const(rb_cPcap, "DLT_PRONET", INT2NUM(DLT_PRONET));
|
||||
rb_define_const(rb_cPcap, "DLT_CHAOS", INT2NUM(DLT_CHAOS));
|
||||
rb_define_const(rb_cPcap, "DLT_IEEE802", INT2NUM(DLT_IEEE802));
|
||||
rb_define_const(rb_cPcap, "DLT_ARCNET", INT2NUM(DLT_ARCNET));
|
||||
rb_define_const(rb_cPcap, "DLT_SLIP", INT2NUM(DLT_SLIP));
|
||||
rb_define_const(rb_cPcap, "DLT_PPP", INT2NUM(DLT_PPP));
|
||||
rb_define_const(rb_cPcap, "DLT_FDDI", INT2NUM(DLT_FDDI));
|
||||
rb_define_const(rb_cPcap, "DLT_ATM_RFC1483", INT2NUM(DLT_ATM_RFC1483));
|
||||
rb_define_const(rb_cPcap, "DLT_RAW", INT2NUM(DLT_RAW));
|
||||
rb_define_const(rb_cPcap, "DLT_SLIP_BSDOS", INT2NUM(DLT_SLIP_BSDOS));
|
||||
rb_define_const(rb_cPcap, "DLT_PPP_BSDOS", INT2NUM(DLT_PPP_BSDOS));
|
||||
rb_define_const(rb_cPcap, "DLT_IEEE802_11", INT2NUM(DLT_IEEE802_11));
|
||||
rb_define_const(rb_cPcap, "DLT_IEEE802_11_RADIO", INT2NUM(DLT_IEEE802_11_RADIO));
|
||||
rb_define_const(rb_cPcap, "DLT_IEEE802_11_RADIO_AVS", INT2NUM(DLT_IEEE802_11_RADIO_AVS));
|
||||
rb_define_const(rb_cPcap, "DLT_LINUX_SLL", INT2NUM(DLT_LINUX_SLL));
|
||||
rb_define_const(rb_cPcap, "DLT_PRISM_HEADER", INT2NUM(DLT_PRISM_HEADER));
|
||||
rb_define_const(rb_cPcap, "DLT_AIRONET_HEADER", INT2NUM(DLT_AIRONET_HEADER));
|
||||
|
||||
rb_define_singleton_method(rb_cPcap, "new", rbpcap_new_s, 0);
|
||||
|
||||
rb_define_singleton_method(rb_cPcap, "open_live", rbpcap_open_live_s, 4);
|
||||
rb_define_singleton_method(rb_cPcap, "open_offline", rbpcap_open_offline_s, 1);
|
||||
rb_define_singleton_method(rb_cPcap, "open_dead", rbpcap_open_dead_s, 2);
|
||||
rb_define_singleton_method(rb_cPcap, "dump_open", rbpcap_dump_open, 1);
|
||||
|
||||
rb_define_method(rb_cPcap, "dump", rbpcap_dump, 3);
|
||||
|
||||
rb_define_method(rb_cPcap, "each", rbpcap_capture, 0);
|
||||
rb_define_method(rb_cPcap, "next", rbpcap_next, 0);
|
||||
rb_define_method(rb_cPcap, "setfilter", rbpcap_setfilter, 1);
|
||||
rb_define_method(rb_cPcap, "inject", rbpcap_inject, 1);
|
||||
rb_define_method(rb_cPcap, "datalink", rbpcap_datalink, 0);
|
||||
|
||||
rb_define_method(rb_cPcap, "snapshot", rbpcap_snapshot, 0);
|
||||
rb_define_method(rb_cPcap, "snaplen", rbpcap_snapshot, 0);
|
||||
rb_define_method(rb_cPcap, "stats", rbpcap_stats, 0);
|
||||
|
||||
|
||||
//Netifaces
|
||||
rb_define_module_function(rb_cPcap, "interfaces", rbnetifaces_s_interfaces, 0);
|
||||
rb_define_module_function(rb_cPcap, "addresses", rbnetifaces_s_addresses, 1);
|
||||
rb_define_module_function(rb_cPcap, "interface_info", rbnetifaces_s_interface_info, 1);
|
||||
|
||||
//constants
|
||||
// Address families (auto-detect using #ifdef)
|
||||
|
||||
#ifdef AF_INET
|
||||
rb_define_const(rb_cPcap, "AF_INET", INT2NUM(AF_INET));
|
||||
#endif
|
||||
#ifdef AF_INET6
|
||||
rb_define_const(rb_cPcap, "AF_INET6", INT2NUM(AF_INET6));
|
||||
#endif
|
||||
#ifdef AF_UNSPEC
|
||||
rb_define_const(rb_cPcap, "AF_UNSPEC", INT2NUM(AF_UNSPEC));
|
||||
#endif
|
||||
#ifdef AF_UNIX
|
||||
rb_define_const(rb_cPcap, "AF_UNIX", INT2NUM(AF_UNIX));
|
||||
#endif
|
||||
#ifdef AF_FILE
|
||||
rb_define_const(rb_cPcap, "AF_FILE", INT2NUM(AF_FILE));
|
||||
#endif
|
||||
|
||||
#ifdef AF_AX25
|
||||
rb_define_const(rb_cPcap, "AF_AX25", INT2NUM(AF_AX25));
|
||||
#endif
|
||||
#ifdef AF_IMPLINK
|
||||
rb_define_const(rb_cPcap, "AF_IMPLINK", INT2NUM(AF_IMPLINK));
|
||||
#endif
|
||||
#ifdef AF_PUP
|
||||
rb_define_const(rb_cPcap, "AF_PUP", INT2NUM(AF_PUP));
|
||||
#endif
|
||||
#ifdef AF_CHAOS
|
||||
rb_define_const(rb_cPcap, "AF_CHAOS", INT2NUM(AF_CHAOS));
|
||||
#endif
|
||||
#ifdef AF_NS
|
||||
rb_define_const(rb_cPcap, "AF_NS", INT2NUM(AF_NS));
|
||||
#endif
|
||||
#ifdef AF_ISO
|
||||
rb_define_const(rb_cPcap, "AF_ISO", INT2NUM(AF_ISO));
|
||||
#endif
|
||||
#ifdef AF_ECMA
|
||||
rb_define_const(rb_cPcap, "AF_ECMA", INT2NUM(AF_ECMA));
|
||||
#endif
|
||||
#ifdef AF_DATAKIT
|
||||
rb_define_const(rb_cPcap, "AF_DATAKIT", INT2NUM(AF_DATAKIT));
|
||||
#endif
|
||||
#ifdef AF_CCITT
|
||||
rb_define_const(rb_cPcap, "AF_CCITT", INT2NUM(AF_CCITT));
|
||||
#endif
|
||||
#ifdef AF_SNA
|
||||
rb_define_const(rb_cPcap, "AF_SNA", INT2NUM(AF_SNA));
|
||||
#endif
|
||||
#ifdef AF_DECnet
|
||||
rb_define_const(rb_cPcap, "AF_DECnet", INT2NUM(AF_DECnet));
|
||||
#endif
|
||||
#ifdef AF_DLI
|
||||
rb_define_const(rb_cPcap, "AF_DLI", INT2NUM(AF_DLI));
|
||||
#endif
|
||||
#ifdef AF_LAT
|
||||
rb_define_const(rb_cPcap, "AF_LAT", INT2NUM(AF_LAT));
|
||||
#endif
|
||||
#ifdef AF_HYLINK
|
||||
rb_define_const(rb_cPcap, "AF_HYLINK", INT2NUM(AF_HYLINK));
|
||||
#endif
|
||||
#ifdef AF_APPLETALK
|
||||
rb_define_const(rb_cPcap, "AF_APPLETALK", INT2NUM(AF_APPLETALK));
|
||||
#endif
|
||||
#ifdef AF_ROUTE
|
||||
rb_define_const(rb_cPcap, "AF_ROUTE", INT2NUM(AF_ROUTE));
|
||||
#endif
|
||||
#ifdef AF_LINK
|
||||
rb_define_const(rb_cPcap, "AF_LINK", INT2NUM(AF_LINK));
|
||||
#endif
|
||||
#ifdef AF_PACKET
|
||||
rb_define_const(rb_cPcap, "AF_PACKET", INT2NUM(AF_PACKET));
|
||||
#endif
|
||||
#ifdef AF_COIP
|
||||
rb_define_const(rb_cPcap, "AF_COIP", INT2NUM(AF_COIP));
|
||||
#endif
|
||||
#ifdef AF_CNT
|
||||
rb_define_const(rb_cPcap, "AF_CNT", INT2NUM(AF_CNT));
|
||||
#endif
|
||||
#ifdef AF_IPX
|
||||
rb_define_const(rb_cPcap, "AF_IPX", INT2NUM(AF_IPX));
|
||||
#endif
|
||||
#ifdef AF_SIP
|
||||
rb_define_const(rb_cPcap, "AF_SIP", INT2NUM(AF_SIP));
|
||||
#endif
|
||||
#ifdef AF_NDRV
|
||||
rb_define_const(rb_cPcap, "AF_NDRV", INT2NUM(AF_NDRV));
|
||||
#endif
|
||||
#ifdef AF_ISDN
|
||||
rb_define_const(rb_cPcap, "AF_ISDN", INT2NUM(AF_ISDN));
|
||||
#endif
|
||||
#ifdef AF_NATM
|
||||
rb_define_const(rb_cPcap, "AF_NATM", INT2NUM(AF_NATM));
|
||||
#endif
|
||||
#ifdef AF_SYSTEM
|
||||
rb_define_const(rb_cPcap, "AF_SYSTEM", INT2NUM(AF_SYSTEM));
|
||||
#endif
|
||||
#ifdef AF_NETBIOS
|
||||
rb_define_const(rb_cPcap, "AF_NETBIOS", INT2NUM(AF_NETBIOS));
|
||||
#endif
|
||||
#ifdef AF_NETBEUI
|
||||
rb_define_const(rb_cPcap, "AF_NETBEUI", INT2NUM(AF_NETBEUI));
|
||||
#endif
|
||||
#ifdef AF_PPP
|
||||
rb_define_const(rb_cPcap, "AF_PPP", INT2NUM(AF_PPP));
|
||||
#endif
|
||||
#ifdef AF_ATM
|
||||
rb_define_const(rb_cPcap, "AF_ATM", INT2NUM(AF_ATM));
|
||||
#endif
|
||||
#ifdef AF_ATMPVC
|
||||
rb_define_const(rb_cPcap, "AF_ATMPVC", INT2NUM(AF_ATMPVC));
|
||||
#endif
|
||||
#ifdef AF_ATMSVC
|
||||
rb_define_const(rb_cPcap, "AF_ATMSVC", INT2NUM(AF_ATMSVC));
|
||||
#endif
|
||||
#ifdef AF_NETGRAPH
|
||||
rb_define_const(rb_cPcap, "AF_NETGRAPH", INT2NUM(AF_NETGRAPH));
|
||||
#endif
|
||||
#ifdef AF_VOICEVIEW
|
||||
rb_define_const(rb_cPcap, "AF_VOICEVIEW", INT2NUM(AF_VOICEVIEW));
|
||||
#endif
|
||||
#ifdef AF_FIREFOX
|
||||
rb_define_const(rb_cPcap, "AF_FIREFOX", INT2NUM(AF_FIREFOX));
|
||||
#endif
|
||||
#ifdef AF_UNKNOWN1
|
||||
rb_define_const(rb_cPcap, "AF_UNKNOWN1", INT2NUM(AF_UNKNOWN1));
|
||||
#endif
|
||||
#ifdef AF_BAN
|
||||
rb_define_const(rb_cPcap, "AF_BAN", INT2NUM(AF_BAN));
|
||||
#endif
|
||||
#ifdef AF_CLUSTER
|
||||
rb_define_const(rb_cPcap, "AF_CLUSTER", INT2NUM(AF_CLUSTER));
|
||||
#endif
|
||||
#ifdef AF_12844
|
||||
rb_define_const(rb_cPcap, "AF_12844", INT2NUM(AF_12844));
|
||||
#endif
|
||||
#ifdef AF_IRDA
|
||||
rb_define_const(rb_cPcap, "AF_IRDA", INT2NUM(AF_IRDA));
|
||||
#endif
|
||||
#ifdef AF_NETDES
|
||||
rb_define_const(rb_cPcap, "AF_NETDES", INT2NUM(AF_NETDES));
|
||||
#endif
|
||||
#ifdef AF_NETROM
|
||||
rb_define_const(rb_cPcap, "AF_NETROM", INT2NUM(AF_NETROM));
|
||||
#endif
|
||||
#ifdef AF_BRIDGE
|
||||
rb_define_const(rb_cPcap, "AF_BRIDGE", INT2NUM(AF_BRIDGE));
|
||||
#endif
|
||||
#ifdef AF_X25
|
||||
rb_define_const(rb_cPcap, "AF_X25", INT2NUM(AF_X25));
|
||||
#endif
|
||||
#ifdef AF_ROSE
|
||||
rb_define_const(rb_cPcap, "AF_ROSE", INT2NUM(AF_ROSE));
|
||||
#endif
|
||||
#ifdef AF_SECURITY
|
||||
rb_define_const(rb_cPcap, "AF_SECURITY", INT2NUM(AF_SECURITY));
|
||||
#endif
|
||||
#ifdef AF_KEY
|
||||
rb_define_const(rb_cPcap, "AF_KEY", INT2NUM(AF_KEY));
|
||||
#endif
|
||||
#ifdef AF_NETLINK
|
||||
rb_define_const(rb_cPcap, "AF_NETLINK", INT2NUM(AF_NETLINK));
|
||||
#endif
|
||||
#ifdef AF_ASH
|
||||
rb_define_const(rb_cPcap, "AF_ASH", INT2NUM(AF_ASH));
|
||||
#endif
|
||||
#ifdef AF_ECONET
|
||||
rb_define_const(rb_cPcap, "AF_ECONET", INT2NUM(AF_ECONET));
|
||||
#endif
|
||||
#ifdef AF_PPPOX
|
||||
rb_define_const(rb_cPcap, "AF_PPPOX", INT2NUM(AF_PPPOX));
|
||||
#endif
|
||||
#ifdef AF_WANPIPE
|
||||
rb_define_const(rb_cPcap, "AF_WANPIPE", INT2NUM(AF_WANPIPE));
|
||||
#endif
|
||||
#ifdef AF_BLUETOOTH
|
||||
rb_define_const(rb_cPcap, "AF_BLUETOOTH", INT2NUM(AF_BLUETOOTH));
|
||||
#endif
|
||||
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
base = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
||||
$:.unshift(File.join(File.dirname(base)))
|
||||
|
||||
require 'test/unit'
|
||||
require 'pcaprub'
|
||||
|
||||
#
|
||||
# Simple unit test, requires r00t.
|
||||
#
|
||||
|
||||
class Pcap::UnitTest < Test::Unit::TestCase
|
||||
|
||||
def test_version
|
||||
assert_equal(String, Pcap.version.class)
|
||||
puts "Pcaprub version: #{Pcap.version}"
|
||||
end
|
||||
|
||||
def test_lookupdev
|
||||
assert_equal(String, Pcap.lookupdev.class)
|
||||
puts "Pcaprub default device: #{Pcap.lookupdev}"
|
||||
end
|
||||
|
||||
def test_lookupnet
|
||||
dev = Pcap.lookupdev
|
||||
assert_equal(Array, Pcap.lookupnet(dev).class)
|
||||
net = Pcap.lookupnet(dev)
|
||||
puts "Pcaprub net (#{dev}): #{net[0]} #{[net[1]].pack("N").unpack("H*")[0]}"
|
||||
end
|
||||
|
||||
def test_pcap_new
|
||||
o = Pcap.new
|
||||
assert_equal(Pcap, o.class)
|
||||
end
|
||||
|
||||
def test_pcap_setfilter_bad
|
||||
e = nil
|
||||
o = Pcap.new
|
||||
begin
|
||||
o.setfilter("not ip")
|
||||
rescue ::Exception => e
|
||||
end
|
||||
|
||||
assert_equal(e.class, ArgumentError)
|
||||
end
|
||||
|
||||
def test_pcap_setfilter
|
||||
d = Pcap.lookupdev
|
||||
o = Pcap.open_live(d, 65535, true, 1)
|
||||
r = o.setfilter("not ip")
|
||||
assert_equal(Pcap, r.class)
|
||||
end
|
||||
|
||||
def test_pcap_inject
|
||||
d = Pcap.lookupdev
|
||||
o = Pcap.open_live(d, 65535, true, 1)
|
||||
r = o.inject("X" * 512)
|
||||
assert_equal(512, r)
|
||||
end
|
||||
|
||||
def test_pcap_datalink
|
||||
d = Pcap.lookupdev
|
||||
o = Pcap.open_live(d, 65535, true, 1)
|
||||
r = o.datalink
|
||||
assert_equal(Fixnum, r.class)
|
||||
end
|
||||
|
||||
def test_pcap_snapshot
|
||||
d = Pcap.lookupdev
|
||||
o = Pcap.open_live(d, 1344, true, 1)
|
||||
r = o.snapshot
|
||||
assert_equal(1344, r)
|
||||
end
|
||||
|
||||
def test_pcap_stats
|
||||
d = Pcap.lookupdev
|
||||
o = Pcap.open_live(d, 1344, true, 1)
|
||||
r = o.stats
|
||||
assert_equal(Hash, r.class)
|
||||
end
|
||||
|
||||
def test_pcap_next
|
||||
d = Pcap.lookupdev
|
||||
o = Pcap.open_live(d, 1344, true, 1)
|
||||
|
||||
@c = 0
|
||||
t = Thread.new { while(true); @c += 1; select(nil, nil, nil, 0.10); end; }
|
||||
|
||||
require 'timeout'
|
||||
begin
|
||||
Timeout.timeout(10) do
|
||||
o.each do |pkt|
|
||||
end
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
end
|
||||
|
||||
t.kill
|
||||
puts "Background thread ticked #{@c} times while capture was running"
|
||||
true
|
||||
end
|
||||
|
||||
def test_netifaces_constants
|
||||
puts "AF_LINK Value is #{Pcap::AF_LINK}"
|
||||
puts "AF_INET Value is #{Pcap::AF_INET}"
|
||||
puts "AF_INET6 Value is #{Pcap::AF_INET6}" if Pcap.const_defined?(:AF_INET6)
|
||||
end
|
||||
|
||||
def test_netifaces_functions
|
||||
Pcap.interfaces.sort.each do |iface|
|
||||
puts "#{iface} :"
|
||||
Pcap.addresses(iface).sort.each do |family,values|
|
||||
puts "\t#{family} :"
|
||||
values.each do |val|
|
||||
puts "\t\taddr : #{val['addr']}" if val.has_key?("addr")
|
||||
puts "\t\tnetmask : #{val['netmask']}" if val.has_key?("netmask")
|
||||
puts "\t\tbroadcast : #{val['broadcast']}" if val.has_key?("broadcast")
|
||||
puts "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
end
|
||||
require 'packetfu/packetfu'
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
== LICENSE
|
||||
|
||||
Copyright (c) 2008-2012, Tod Beardsley
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Tod Beardsley nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY TOD BEARDSLEY ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL TOD BEARDSLEY BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
# :title: PacketFu Documentation
|
||||
# :main: README
|
||||
|
||||
cwd = File.expand_path(File.dirname(__FILE__))
|
||||
|
||||
$: << cwd
|
||||
|
||||
require File.join(cwd,"packetfu","structfu")
|
||||
require "ipaddr"
|
||||
require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
|
||||
|
||||
module PacketFu
|
||||
|
||||
# Picks up all the protocols defined in the protos subdirectory
|
||||
def self.require_protos(cwd)
|
||||
protos_dir = File.join(cwd, "packetfu", "protos")
|
||||
Dir.new(protos_dir).each do |fname|
|
||||
next unless fname[/\.rb$/]
|
||||
begin
|
||||
require File.join(protos_dir,fname)
|
||||
rescue
|
||||
warn "Warning: Could not load `#{fname}'. Skipping."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Deal with Ruby's encoding by ignoring it.
|
||||
def self.force_binary(str)
|
||||
str.force_encoding "binary" if str.respond_to? :force_encoding
|
||||
end
|
||||
|
||||
# Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
|
||||
@byte_order = :little
|
||||
|
||||
# Checks if pcaprub is loaded correctly.
|
||||
@pcaprub_loaded = false
|
||||
|
||||
# PacketFu works best with Pcaprub version 0.8-dev (at least)
|
||||
# The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
|
||||
def self.pcaprub_platform_require
|
||||
begin
|
||||
require 'pcaprub'
|
||||
rescue LoadError
|
||||
return false
|
||||
end
|
||||
@pcaprub_loaded = true
|
||||
end
|
||||
|
||||
pcaprub_platform_require
|
||||
|
||||
if @pcaprub_loaded
|
||||
pcaprub_regex = /[0-9]\.([8-9]|[1-7][0-9])(-dev)?/ # Regex for 0.8 and beyond.
|
||||
if Pcap.version !~ pcaprub_regex
|
||||
@pcaprub_loaded = false # Don't bother with broken versions
|
||||
raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
|
||||
end
|
||||
require "packetfu/capture"
|
||||
require "packetfu/inject"
|
||||
end
|
||||
|
||||
# Returns the status of pcaprub
|
||||
def self.pcaprub_loaded?
|
||||
@pcaprub_loaded
|
||||
end
|
||||
|
||||
# Returns an array of classes defined in PacketFu
|
||||
def self.classes
|
||||
constants.map { |const| const_get(const) if const_get(const).kind_of? Class}.compact
|
||||
end
|
||||
|
||||
# Adds the class to PacketFu's list of packet classes -- used in packet parsing.
|
||||
def self.add_packet_class(klass)
|
||||
raise "Need a class" unless klass.kind_of? Class
|
||||
if klass.name !~ /[A-Za-z0-9]Packet/
|
||||
raise "Packet classes should be named 'ProtoPacket'"
|
||||
end
|
||||
@packet_classes ||= []
|
||||
@packet_classes << klass
|
||||
@packet_classes.sort! {|x,y| x.name <=> y.name}
|
||||
end
|
||||
|
||||
# Presumably, there may be a time where you'd like to remove a packet class.
|
||||
def self.remove_packet_class(klass)
|
||||
raise "Need a class" unless klass.kind_of? Class
|
||||
@packet_classes ||= []
|
||||
@packet_classes.delete klass
|
||||
@packet_classes
|
||||
end
|
||||
|
||||
# Returns an array of packet classes
|
||||
def self.packet_classes
|
||||
@packet_classes || []
|
||||
end
|
||||
|
||||
# Returns an array of packet types by packet prefix.
|
||||
def self.packet_prefixes
|
||||
return [] unless @packet_classes
|
||||
@packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
|
||||
end
|
||||
|
||||
# The current inspect style. One of :hex, :dissect, or :default
|
||||
# Note that :default means Ruby's default, which is usually
|
||||
# far too long to be useful.
|
||||
def self.inspect_style
|
||||
@inspect_style ||= :dissect
|
||||
end
|
||||
|
||||
# Setter for PacketFu's @inspect_style
|
||||
def self.inspect_style=(arg)
|
||||
@inspect_style = case arg
|
||||
when :hex, :pretty
|
||||
:hex
|
||||
when :dissect, :verbose
|
||||
:dissect
|
||||
when :default, :ugly
|
||||
:default
|
||||
else
|
||||
:dissect
|
||||
end
|
||||
end
|
||||
|
||||
# Switches inspect styles in a round-robin fashion between
|
||||
# :dissect, :default, and :hex
|
||||
def toggle_inspect
|
||||
case @inspect_style
|
||||
when :hex, :pretty
|
||||
@inspect_style = :dissect
|
||||
when :dissect, :verbose
|
||||
@inspect_style = :default
|
||||
when :default, :ugly
|
||||
@inspect_style = :hex
|
||||
else
|
||||
@inspect_style = :dissect
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
require File.join(cwd,"packetfu","version")
|
||||
require File.join(cwd,"packetfu","pcap")
|
||||
require File.join(cwd,"packetfu","packet")
|
||||
PacketFu.require_protos(cwd)
|
||||
require File.join(cwd,"packetfu","utils")
|
||||
require File.join(cwd,"packetfu","config")
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,170 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# The Capture class is used to construct PcapRub objects in order to collect
|
||||
# packets from an interface.
|
||||
#
|
||||
# This class requires PcapRub. In addition, you will need root (or root-like) privileges
|
||||
# in order to capture from the interface.
|
||||
#
|
||||
# Note, on some wireless cards, setting :promisc => true will disable capturing.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# # Typical use
|
||||
# cap = PacketFu::Capture.new(:iface => 'eth0', :promisc => true)
|
||||
# cap.start
|
||||
# sleep 10
|
||||
# cap.save
|
||||
# first_packet = cap.array[0]
|
||||
#
|
||||
# # Tcpdump-like use
|
||||
# cap = PacketFu::Capture.new(:start => true)
|
||||
# cap.show_live(:save => true, :filter => 'tcp and not port 22')
|
||||
#
|
||||
# == See Also
|
||||
#
|
||||
# Read, Write
|
||||
class Capture
|
||||
attr_accessor :array, :stream # Leave these public and open.
|
||||
attr_reader :iface, :snaplen, :promisc, :timeout # Cant change after the init.
|
||||
|
||||
def initialize(args={})
|
||||
@array = [] # Where the packet array goes.
|
||||
@stream = [] # Where the stream goes.
|
||||
@iface = (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo").to_s
|
||||
@snaplen = args[:snaplen] || 0xffff
|
||||
@promisc = args[:promisc] || false # Sensible for some Intel wifi cards
|
||||
@timeout = args[:timeout] || 1
|
||||
|
||||
setup_params(args)
|
||||
end
|
||||
|
||||
# Used by new().
|
||||
def setup_params(args={})
|
||||
filter = args[:filter] # Not global; filter criteria can change.
|
||||
start = args[:start] || false
|
||||
capture if start
|
||||
bpf(:filter=>filter) if filter
|
||||
end
|
||||
|
||||
# capture() initializes the @stream varaible. Valid arguments are:
|
||||
#
|
||||
# :filter
|
||||
# Provide a bpf filter to enable for the capture. For example, 'ip and not tcp'
|
||||
# :start
|
||||
# When true, start capturing packets to the @stream variable. Defaults to true
|
||||
def capture(args={})
|
||||
if Process.euid.zero?
|
||||
filter = args[:filter]
|
||||
start = args[:start] || true
|
||||
if start
|
||||
begin
|
||||
@stream = Pcap.open_live(@iface,@snaplen,@promisc,@timeout)
|
||||
rescue RuntimeError
|
||||
$stderr.print "Are you sure you're root? Error: "
|
||||
raise
|
||||
end
|
||||
bpf(:filter=>filter) if filter
|
||||
else
|
||||
@stream = []
|
||||
end
|
||||
@stream
|
||||
else
|
||||
raise RuntimeError,"Not root, so can't capture packets. Error: "
|
||||
end
|
||||
end
|
||||
|
||||
# start() is equivalent to capture().
|
||||
def start(args={})
|
||||
capture(args)
|
||||
end
|
||||
|
||||
# clear() clears the @stream and @array variables, essentially starting the
|
||||
# capture session over. Valid arguments are:
|
||||
#
|
||||
# :array
|
||||
# If true, the @array is cleared.
|
||||
# :stream
|
||||
# If true, the @stream is cleared.
|
||||
def clear(args={})
|
||||
array = args[:array] || true
|
||||
stream = args[:stream] || true
|
||||
@array = [] if array
|
||||
@stream = [] if stream
|
||||
end
|
||||
|
||||
# bpf() sets a bpf filter on a capture session. Valid arugments are:
|
||||
#
|
||||
# :filter
|
||||
# Provide a bpf filter to enable for the capture. For example, 'ip and not tcp'
|
||||
def bpf(args={})
|
||||
filter = args[:filter]
|
||||
capture if @stream.class == Array
|
||||
@stream.setfilter(filter)
|
||||
end
|
||||
|
||||
# wire_to_array() saves a packet stream as an array of binary strings. From here,
|
||||
# packets may accessed by other functions. Note that the wire_to_array empties
|
||||
# the stream, so multiple calls will append new packets to @array.
|
||||
# Valid arguments are:
|
||||
#
|
||||
# :filter
|
||||
# Provide a bpf filter to apply to packets moving from @stream to @array.
|
||||
def wire_to_array(args={})
|
||||
filter = args[:filter]
|
||||
bpf(:filter=>filter) if filter
|
||||
|
||||
while this_pkt = @stream.next
|
||||
@array << this_pkt
|
||||
end
|
||||
@array.size
|
||||
end
|
||||
|
||||
# next() exposes the Stream object's next method to the outside world.
|
||||
def next
|
||||
return @stream.next
|
||||
end
|
||||
|
||||
# w2a() is a equivalent to wire_to_array()
|
||||
def w2a(args={})
|
||||
wire_to_array(args)
|
||||
end
|
||||
|
||||
# save() is a equivalent to wire_to_array()
|
||||
def save(args={})
|
||||
wire_to_array(args)
|
||||
end
|
||||
|
||||
# show_live() is a method to capture packets and display peek() data to stdout. Valid arguments are:
|
||||
#
|
||||
# :filter
|
||||
# Provide a bpf filter to captured packets.
|
||||
# :save
|
||||
# Save the capture in @array
|
||||
# :verbose
|
||||
# TODO: Not implemented yet; do more than just peek() at the packets.
|
||||
# :quiet
|
||||
# TODO: Not implemented yet; do less than peek() at the packets.
|
||||
def show_live(args={})
|
||||
filter = args[:filter]
|
||||
save = args[:save]
|
||||
verbose = args[:verbose] || args[:v] || false
|
||||
quiet = args[:quiet] || args[:q] || false # Setting q and v doesn't make a lot of sense but hey.
|
||||
|
||||
# Ensure the capture's started.
|
||||
if @stream.class == Array
|
||||
capture
|
||||
end
|
||||
|
||||
@stream.setfilter(filter) if filter
|
||||
while true
|
||||
@stream.each do |pkt|
|
||||
puts Packet.parse(pkt).peek
|
||||
@array << pkt if args[:save]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# The Config class holds various bits of useful default information
|
||||
# for packet creation. If initialized without arguments, @iface will be
|
||||
# set to ENV['IFACE'] or Pcap.lookupdev (or lo), and the @pcapfile will
|
||||
# be set to "/tmp/out.pcap" # (yes, it's Linux-biased, sorry, fixing
|
||||
# this is a TODO.)
|
||||
#
|
||||
# Any number of instance variables can be passed in to the intialize function (as a
|
||||
# hash), though only the expected network-related variables will be readable and
|
||||
# writeable directly.
|
||||
#
|
||||
# == Examples
|
||||
#
|
||||
# PacketFu::Config.new(:ip_saddr => "1.2.3.4").ip_saddr #=> "1.2.3.4"
|
||||
# PacketFu::Config.new(:foo=>"bar").foo #=> NomethodError: undefined method `foo'...
|
||||
#
|
||||
# The config() function, however, does provide access to custom variables:
|
||||
#
|
||||
# PacketFu::Config.new(:foo=>"bar").config[:foo] #=> "bar"
|
||||
# obj = PacketFu::Config.new(:foo=>"bar")
|
||||
# obj.config(:baz => "bat")
|
||||
# obj.config #=> {:iface=>"eth0", :baz=>"bat", :pcapfile=>"/tmp/out.pcap", :foo=>"bar"}
|
||||
class Config
|
||||
attr_accessor :eth_saddr, # The discovered eth_saddr
|
||||
:eth_daddr, # The discovered eth_daddr (ie, the gateway)
|
||||
:eth_src, # The discovered eth_src in binary form.
|
||||
:eth_dst, # The discovered eth_dst (gateway) in binary form.
|
||||
:ip_saddr, # The discovered ip_saddr
|
||||
:ip_src, # The discovered ip_src in binary form.
|
||||
:iface, # The declared interface.
|
||||
:pcapfile # A declared default file to write to.
|
||||
|
||||
def initialize(args={})
|
||||
if Process.euid.zero?
|
||||
@iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo"
|
||||
end
|
||||
@pcapfile = "/tmp/out.pcap"
|
||||
args.each_pair { |k,v| self.instance_variable_set(("@#{k}"),v) }
|
||||
end
|
||||
|
||||
# Returns all instance variables as a hash (including custom variables set at initialization).
|
||||
def config(arg=nil)
|
||||
if arg
|
||||
arg.each_pair {|k,v| self.instance_variable_set(("@" + k.to_s).intern, v)}
|
||||
else
|
||||
config_hash = {}
|
||||
self.instance_variables.each do |v|
|
||||
key = v.to_s.gsub(/^@/,"").to_sym
|
||||
config_hash[key] = self.instance_variable_get(v)
|
||||
end
|
||||
config_hash
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,66 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# The Inject class handles injecting arrays of binary data on the wire.
|
||||
#
|
||||
# To inject single packets, use PacketFu::Packet.to_w() instead.
|
||||
class Inject
|
||||
attr_accessor :array, :stream, :show_live # Leave these public and open.
|
||||
attr_reader :iface, :snaplen, :promisc, :timeout # Cant change after the init.
|
||||
|
||||
def initialize(args={})
|
||||
@array = [] # Where the packet array goes.
|
||||
@stream = [] # Where the stream goes.
|
||||
@iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo"
|
||||
@snaplen = args[:snaplen] || 0xffff
|
||||
@promisc = args[:promisc] || false # Sensible for some Intel wifi cards
|
||||
@timeout = args[:timeout] || 1
|
||||
@show_live = nil
|
||||
end
|
||||
|
||||
# Takes an array, and injects them onto an interface. Note that
|
||||
# complete packets (Ethernet headers on down) are expected.
|
||||
#
|
||||
# === Parameters
|
||||
#
|
||||
# :array || arr
|
||||
# An array of binary data (usually packet.to_s style).
|
||||
# :int || sleep
|
||||
# Number of seconds to sleep between injections (in float format)
|
||||
# :show_live || :live
|
||||
# If true, puts data about what was injected to stdout.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# inj = PacketFu::Inject.new
|
||||
# inj.array_to_wire(:array => [pkt1, pkt2, pkt3], :sleep => 0.1)
|
||||
#
|
||||
def array_to_wire(args={})
|
||||
pkt_array = args[:array] || args[:arr] || @array
|
||||
interval = args[:int] || args[:sleep]
|
||||
show_live = args[:show_live] || args[:live] || @show_live
|
||||
|
||||
@stream = Pcap.open_live(@iface,@snaplen,@promisc,@timeout)
|
||||
pkt_count = 0
|
||||
pkt_array.each do |pkt|
|
||||
@stream.inject(pkt)
|
||||
sleep interval if interval
|
||||
pkt_count +=1
|
||||
puts "Sent Packet \##{pkt_count} (#{pkt.size})" if show_live
|
||||
end
|
||||
# Return # of packets sent, array size, and array total size
|
||||
[pkt_count, pkt_array.size, pkt_array.join.size]
|
||||
end
|
||||
|
||||
# Equivalent to array_to_wire
|
||||
def a2w(args={})
|
||||
array_to_wire(args)
|
||||
end
|
||||
|
||||
# Equivalent to array_to_wire
|
||||
def inject(args={})
|
||||
array_to_wire(args)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,534 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# Packet is the parent class of EthPacket, IPPacket, UDPPacket, TCPPacket, and all
|
||||
# other packets. It acts as both a singleton class, so things like
|
||||
# Packet.parse can happen, and as an abstract class to provide
|
||||
# subclasses some structure.
|
||||
class Packet
|
||||
|
||||
attr_reader :flavor # Packet Headers are responsible for their own specific flavor methods.
|
||||
attr_accessor :headers # All packets have a header collection, useful for determining protocol trees.
|
||||
attr_accessor :iface # Default inferface to send packets to
|
||||
attr_accessor :inspect_style # Default is :dissect, can also be :hex or :default
|
||||
|
||||
# Register subclasses in PacketFu.packet_class to do all kinds of neat things
|
||||
# that obviates those long if/else trees for parsing. It's pretty sweet.
|
||||
def self.inherited(subclass)
|
||||
PacketFu.add_packet_class(subclass)
|
||||
end
|
||||
|
||||
# Force strings into binary.
|
||||
def self.force_binary(str)
|
||||
str.force_encoding "binary" if str.respond_to? :force_encoding
|
||||
end
|
||||
|
||||
# Parse() creates the correct packet type based on the data, and returns the apporpiate
|
||||
# Packet subclass object.
|
||||
#
|
||||
# There is an assumption here that all incoming packets are either EthPacket
|
||||
# or InvalidPacket types. This will be addressed pretty soon.
|
||||
#
|
||||
# If application-layer parsing is /not/ desired, that should be indicated explicitly
|
||||
# with an argument of :parse_app => false. Otherwise, app-layer parsing will happen.
|
||||
#
|
||||
# It is no longer neccisary to manually add packet types here.
|
||||
def self.parse(packet=nil,args={})
|
||||
parse_app = true if(args[:parse_app].nil? or args[:parse_app])
|
||||
force_binary(packet)
|
||||
if parse_app
|
||||
classes = PacketFu.packet_classes.select {|pclass| pclass.can_parse? packet}
|
||||
else
|
||||
classes = PacketFu.packet_classes.select {|pclass| pclass.can_parse? packet}.reject {|pclass| pclass.layer_symbol == :application}
|
||||
end
|
||||
p = classes.sort {|x,y| x.layer <=> y.layer}.last.new
|
||||
parsed_packet = p.read(packet,args)
|
||||
end
|
||||
|
||||
def handle_is_identity(ptype)
|
||||
idx = PacketFu.packet_prefixes.index(ptype.to_s.downcase)
|
||||
if idx
|
||||
self.kind_of? PacketFu.packet_classes[idx]
|
||||
else
|
||||
raise NoMethodError, "Undefined method `is_#{ptype}?' for #{self.class}."
|
||||
end
|
||||
end
|
||||
|
||||
# Get the binary string of the entire packet.
|
||||
def to_s
|
||||
@headers[0].to_s
|
||||
end
|
||||
|
||||
# In the event of no proper decoding, at least send it to the inner-most header.
|
||||
def write(io)
|
||||
@headers[0].write(io)
|
||||
end
|
||||
|
||||
# Get the outermost payload (body) of the packet; this is why all packet headers
|
||||
# should have a body type.
|
||||
def payload
|
||||
@headers.last.body
|
||||
end
|
||||
|
||||
# Set the outermost payload (body) of the packet.
|
||||
def payload=(args)
|
||||
@headers.last.body=(args)
|
||||
end
|
||||
|
||||
# Converts a packet to libpcap format. Bit of a hack?
|
||||
def to_pcap(args={})
|
||||
p = PcapPacket.new(:endian => args[:endian],
|
||||
:timestamp => Timestamp.new.to_s,
|
||||
:incl_len => self.to_s.size,
|
||||
:orig_len => self.to_s.size,
|
||||
:data => self)
|
||||
end
|
||||
|
||||
# Put the entire packet into a libpcap file. XXX: this is a
|
||||
# hack for now just to confirm that packets are getting created
|
||||
# correctly. Now with append! XXX: Document this!
|
||||
def to_f(filename=nil,mode='w')
|
||||
filename ||= 'out.pcap'
|
||||
mode = mode.to_s[0,1] + "b"
|
||||
raise ArgumentError, "Unknown mode: #{mode.to_s}" unless mode =~ /^[wa]/
|
||||
if(mode == 'w' || !(File.exists?(filename)))
|
||||
data = [PcapHeader.new, self.to_pcap].map {|x| x.to_s}.join
|
||||
else
|
||||
data = self.to_pcap
|
||||
end
|
||||
File.open(filename, mode) {|f| f.write data}
|
||||
return [filename, 1, data.size]
|
||||
end
|
||||
|
||||
# Put the entire packet on the wire by creating a temporary PacketFu::Inject object.
|
||||
# TODO: Do something with auto-checksumming?
|
||||
def to_w(iface=nil)
|
||||
iface = (iface || self.iface || PacketFu::Config.new.config[:iface]).to_s
|
||||
inj = PacketFu::Inject.new(:iface => iface)
|
||||
inj.array = [@headers[0].to_s]
|
||||
inj.inject
|
||||
end
|
||||
|
||||
# Recalculates all the calcuated fields for all headers in the packet.
|
||||
# This is important since read() wipes out all the calculated fields
|
||||
# such as length and checksum and what all.
|
||||
def recalc(arg=:all)
|
||||
case arg
|
||||
when :ip
|
||||
ip_recalc(:all)
|
||||
when :icmp
|
||||
icmp_recalc(:all)
|
||||
when :udp
|
||||
udp_recalc(:all)
|
||||
when :tcp
|
||||
tcp_recalc(:all)
|
||||
when :all
|
||||
ip_recalc(:all) if @ip_header
|
||||
icmp_recalc(:all) if @icmp_header
|
||||
udp_recalc(:all) if @udp_header
|
||||
tcp_recalc(:all) if @tcp_header
|
||||
else
|
||||
raise ArgumentError, "Recalculating `#{arg}' unsupported. Try :all"
|
||||
end
|
||||
@headers[0]
|
||||
end
|
||||
|
||||
# Read() takes (and trusts) the io input and shoves it all into a well-formed Packet.
|
||||
# Note that read is a destructive process, so any existing data will be lost.
|
||||
#
|
||||
# A note on the :strip => true argument: If :strip is set, defined lengths of data will
|
||||
# be believed, and any trailers (such as frame check sequences) will be chopped off. This
|
||||
# helps to ensure well-formed packets, at the cost of losing perhaps important FCS data.
|
||||
#
|
||||
# If :strip is false, header lengths are /not/ believed, and all data will be piped in.
|
||||
# When capturing from the wire, this is usually fine, but recalculating the length before
|
||||
# saving or re-transmitting will absolutely change the data payload; FCS data will become
|
||||
# part of the TCP data as far as tcp_len is concerned. Some effort has been made to preserve
|
||||
# the "real" payload for the purposes of checksums, but currently, it's impossible to seperate
|
||||
# new payload data from old trailers, so things like pkt.payload += "some data" will not work
|
||||
# correctly.
|
||||
#
|
||||
# So, to summarize; if you intend to alter the data, use :strip. If you don't, don't. Also,
|
||||
# this is a horrid hack. Stripping is useful (and fun!), but the default behavior really
|
||||
# should be to create payloads correctly, and /not/ treat extra FCS data as a payload.
|
||||
#
|
||||
# Finally, packet subclasses should take two arguments: the string that is the data
|
||||
# to be transmuted into a packet, as well as args. This superclass method is merely
|
||||
# concerned with handling args common to many packet formats (namely, fixing packets
|
||||
# on the fly)
|
||||
def read(args={})
|
||||
if args[:fix] || args[:recalc]
|
||||
ip_recalc(:ip_sum) if self.is_ip?
|
||||
recalc(:tcp) if self.is_tcp?
|
||||
recalc(:udp) if self.is_udp?
|
||||
end
|
||||
end
|
||||
|
||||
# Packets are bundles of lots of objects, so copying them
|
||||
# is a little complicated -- a dup of a packet is actually
|
||||
# full of pass-by-reference stuff in the @headers, so
|
||||
# if you change one, you're changing all this copies, too.
|
||||
#
|
||||
# Normally, this doesn't seem to be a big deal, and it's
|
||||
# a pretty decent performance tradeoff. But, if you're going
|
||||
# to be creating a template packet to base a bunch of slightly
|
||||
# different ones off of (like a fuzzer might), you'll want
|
||||
# to use clone()
|
||||
def clone
|
||||
Packet.parse(self.to_s)
|
||||
end
|
||||
|
||||
# If two packets are represented as the same binary string, and
|
||||
# they're both actually PacketFu packets of the same sort, they're equal.
|
||||
#
|
||||
# The intuitive result is that a packet of a higher layer (like DNSPacket)
|
||||
# can be equal to a packet of a lower level (like UDPPacket) as long as
|
||||
# the bytes are equal (this can come up if a transport-layer packet has
|
||||
# a hand-crafted payload that is identical to what would have been created
|
||||
# by using an application layer packet)
|
||||
def ==(other)
|
||||
return false unless other.kind_of? self.class
|
||||
return false unless other.respond_to? :to_s
|
||||
self.to_s == other.to_s
|
||||
end
|
||||
|
||||
# Peek provides summary data on packet contents.
|
||||
#
|
||||
# Each packet type should provide a peek_format.
|
||||
def peek(args={})
|
||||
idx = @headers.reverse.map {|h| h.respond_to? peek_format}.index(true)
|
||||
if idx
|
||||
@headers.reverse[idx].peek_format
|
||||
else
|
||||
peek_format
|
||||
end
|
||||
end
|
||||
|
||||
# The peek_format is used to display a single line
|
||||
# of packet data useful for eyeballing. It should not exceed
|
||||
# 80 characters. The Packet superclass defines an example
|
||||
# peek_format, but it should hardly ever be triggered, since
|
||||
# peek traverses the @header list in reverse to find a suitable
|
||||
# format.
|
||||
#
|
||||
# === Format
|
||||
#
|
||||
# * A one or two character protocol initial. It should be unique
|
||||
# * The packet size
|
||||
# * Useful data in a human-usable form.
|
||||
#
|
||||
# Ideally, related peek_formats will all line up with each other
|
||||
# when printed to the screen.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# tcp_packet.peek
|
||||
# #=> "T 1054 10.10.10.105:55000 -> 192.168.145.105:80 [......] S:adc7155b|I:8dd0"
|
||||
# tcp_packet.peek.size
|
||||
# #=> 79
|
||||
#
|
||||
def peek_format
|
||||
peek_data = ["? "]
|
||||
peek_data << "%-5d" % self.to_s.size
|
||||
peek_data << "%68s" % self.to_s[0,34].unpack("H*")[0]
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
# Defines the layer this packet type lives at, based on the number of headers it
|
||||
# requires. Note that this has little to do with the OSI model, since TCP/IP
|
||||
# doesn't really have Session and Presentation layers.
|
||||
#
|
||||
# Ethernet and the like are layer 1, IP, IPv6, and ARP are layer 2,
|
||||
# TCP, UDP, and other transport protocols are layer 3, and application
|
||||
# protocols are at layer 4 or higher. InvalidPackets have an arbitrary
|
||||
# layer 0 to distinguish them.
|
||||
#
|
||||
# Because these don't change much, it's cheaper just to case through them,
|
||||
# and only resort to counting headers if we don't have a match -- this
|
||||
# makes adding protocols somewhat easier, but of course you can just
|
||||
# override this method over there, too. This is merely optimized
|
||||
# for the most likely protocols you see on the Internet.
|
||||
def self.layer
|
||||
case self.name # Lol ran into case's fancy treatment of classes
|
||||
when /InvalidPacket$/; 0
|
||||
when /EthPacket$/; 1
|
||||
when /IPPacket$/, /ARPPacket$/, /IPv6Packet$/; 2
|
||||
when /TCPPacket$/, /UDPPacket$/, /ICMPPacket$/; 3
|
||||
when /HSRPPacket$/; 4
|
||||
else; self.new.headers.size
|
||||
end
|
||||
end
|
||||
|
||||
def layer
|
||||
self.class.layer
|
||||
end
|
||||
|
||||
def self.layer_symbol
|
||||
case self.layer
|
||||
when 0; :invalid
|
||||
when 1; :link
|
||||
when 2; :internet
|
||||
when 3; :transport
|
||||
else; :application
|
||||
end
|
||||
end
|
||||
|
||||
def layer_symbol
|
||||
self.class.layer_symbol
|
||||
end
|
||||
|
||||
# Packet subclasses must override this, since the Packet superclass
|
||||
# can't actually parse anything.
|
||||
def self.can_parse?(str)
|
||||
false
|
||||
end
|
||||
|
||||
# Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
|
||||
def hexify(str)
|
||||
str.force_encoding("ASCII-8BIT") if str.respond_to? :force_encoding
|
||||
hexascii_lines = str.to_s.unpack("H*")[0].scan(/.{1,32}/)
|
||||
regex = Regexp.new('[\x00-\x1f\x7f-\xff]', nil, 'n')
|
||||
chars = str.to_s.gsub(regex,'.')
|
||||
chars_lines = chars.scan(/.{1,16}/)
|
||||
ret = []
|
||||
hexascii_lines.size.times {|i| ret << "%-48s %s" % [hexascii_lines[i].gsub(/(.{2})/,"\\1 "),chars_lines[i]]}
|
||||
ret.join("\n")
|
||||
end
|
||||
|
||||
# If @inspect_style is :default (or :ugly), the inspect output is the usual
|
||||
# inspect.
|
||||
#
|
||||
# If @inspect_style is :hex (or :pretty), the inspect output is
|
||||
# a much more compact hexdump-style, with a shortened set of packet header
|
||||
# names at the top.
|
||||
#
|
||||
# If @inspect_style is :dissect (or :verbose), the inspect output is the
|
||||
# longer, but more readable, dissection of the packet. This is the default.
|
||||
#
|
||||
# TODO: Have an option for colors. Everyone loves colorized irb output.
|
||||
def inspect_hex(arg=0)
|
||||
case arg
|
||||
when :layers
|
||||
ret = []
|
||||
@headers.size.times do |i|
|
||||
ret << hexify(@headers[i])
|
||||
end
|
||||
ret
|
||||
when (0..9)
|
||||
if @headers[arg]
|
||||
hexify(@headers[arg])
|
||||
else
|
||||
nil
|
||||
end
|
||||
when :all
|
||||
inspect_hex(0)
|
||||
end
|
||||
end
|
||||
|
||||
def dissection_table
|
||||
table = []
|
||||
@headers.each_with_index do |header,table_idx|
|
||||
proto = header.class.name.sub(/^.*::/,"")
|
||||
table << [proto,[]]
|
||||
header.class.members.each do |elem|
|
||||
elem_sym = elem.to_sym # to_sym needed for 1.8
|
||||
next if elem_sym == :body
|
||||
elem_type_value = []
|
||||
elem_type_value[0] = elem
|
||||
readable_element = "#{elem}_readable"
|
||||
if header.respond_to? readable_element
|
||||
elem_type_value[1] = header.send(readable_element)
|
||||
else
|
||||
elem_type_value[1] = header.send(elem)
|
||||
end
|
||||
elem_type_value[2] = header[elem.to_sym].class.name
|
||||
table[table_idx][1] << elem_type_value
|
||||
end
|
||||
end
|
||||
table
|
||||
if @headers.last.members.map {|x| x.to_sym }.include? :body
|
||||
body_part = [:body, self.payload, @headers.last.body.class.name]
|
||||
end
|
||||
table << body_part
|
||||
end
|
||||
|
||||
# Renders the dissection_table suitable for screen printing. Can take
|
||||
# one or two arguments. If just the one, only that layer will be displayed
|
||||
# take either a range or a number -- if a range, only protos within
|
||||
# that range will be rendered. If an integer, only that proto
|
||||
# will be rendered.
|
||||
def dissect
|
||||
dtable = self.dissection_table
|
||||
hex_body = nil
|
||||
if dtable.last.kind_of?(Array) and dtable.last.first == :body
|
||||
body = dtable.pop
|
||||
hex_body = hexify(body[1])
|
||||
end
|
||||
elem_widths = [0,0,0]
|
||||
dtable.each do |proto_table|
|
||||
proto_table[1].each do |elems|
|
||||
elems.each_with_index do |e,i|
|
||||
width = e.size
|
||||
elem_widths[i] = width if width > elem_widths[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
total_width = elem_widths.inject(0) {|sum,x| sum+x}
|
||||
table = ""
|
||||
dtable.each do |proto|
|
||||
table << "--"
|
||||
table << proto[0]
|
||||
if total_width > proto[0].size
|
||||
table << ("-" * (total_width - proto[0].size + 2))
|
||||
else
|
||||
table << ("-" * (total_width + 2))
|
||||
end
|
||||
table << "\n"
|
||||
proto[1].each do |elems|
|
||||
table << " "
|
||||
elems_table = []
|
||||
(0..2).each do |i|
|
||||
elems_table << ("%-#{elem_widths[i]}s" % elems[i])
|
||||
end
|
||||
table << elems_table.join("\s")
|
||||
table << "\n"
|
||||
end
|
||||
end
|
||||
if hex_body && !hex_body.empty?
|
||||
table << "-" * 66
|
||||
table << "\n"
|
||||
table << "00-01-02-03-04-05-06-07-08-09-0a-0b-0c-0d-0e-0f---0123456789abcdef\n"
|
||||
table << "-" * 66
|
||||
table << "\n"
|
||||
table << hex_body
|
||||
end
|
||||
table
|
||||
end
|
||||
|
||||
alias :orig_kind_of? :kind_of?
|
||||
|
||||
def kind_of?(klass)
|
||||
return true if orig_kind_of? klass
|
||||
packet_types = proto.map {|p| PacketFu.const_get("#{p}Packet")}
|
||||
match = false
|
||||
packet_types.each do |p|
|
||||
if p.ancestors.include? klass
|
||||
match = true
|
||||
break
|
||||
end
|
||||
end
|
||||
return match
|
||||
end
|
||||
|
||||
# For packets, inspect is overloaded as inspect_hex(0).
|
||||
# Not sure if this is a great idea yet, but it sure makes
|
||||
# the irb output more sane.
|
||||
#
|
||||
# If you hate this, you can run PacketFu.toggle_inspect to return
|
||||
# to the typical (and often unreadable) Object#inspect format.
|
||||
def inspect
|
||||
case @inspect_style
|
||||
when :dissect
|
||||
self.dissect
|
||||
when :hex
|
||||
self.proto.join("|") + "\n" + self.inspect_hex
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the size of the packet (as a binary string)
|
||||
def size
|
||||
self.to_s.size
|
||||
end
|
||||
|
||||
# Returns an array of protocols contained in this packet. For example:
|
||||
#
|
||||
# t = PacketFu::TCPPacket.new
|
||||
# => 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
|
||||
# 00 28 3c ab 00 00 ff 06 7f 25 00 00 00 00 00 00 .(<......%......
|
||||
# 00 00 93 5e 00 00 ad 4f e4 a4 00 00 00 00 50 00 ...^...O......P.
|
||||
# 40 00 4a 92 00 00 @.J...
|
||||
# t.proto
|
||||
# => ["Eth", "IP", "TCP"]
|
||||
#
|
||||
def proto
|
||||
type_array = []
|
||||
self.headers.each {|header| type_array << header.class.to_s.split('::').last.gsub(/Header$/,'')}
|
||||
type_array
|
||||
end
|
||||
|
||||
alias_method :protocol, :proto
|
||||
alias_method :length, :size
|
||||
|
||||
# the Packet class should not be instantiated directly, since it's an
|
||||
# abstract class that real packet types inherit from. Sadly, this
|
||||
# makes the Packet class more difficult to test directly.
|
||||
def initialize(args={})
|
||||
if self.class.name =~ /(::|^)PacketFu::Packet$/
|
||||
raise NoMethodError, "method `new' called for abstract class #{self.class.name}"
|
||||
end
|
||||
@inspect_style = args[:inspect_style] || PacketFu.inspect_style || :dissect
|
||||
if args[:config]
|
||||
args[:config].each_pair do |k,v|
|
||||
case k
|
||||
when :eth_daddr; @eth_header.eth_daddr=v if @eth_header
|
||||
when :eth_saddr; @eth_header.eth_saddr=v if @eth_header
|
||||
when :ip_saddr; @ip_header.ip_saddr=v if @ip_header
|
||||
when :iface; @iface = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Delegate to PacketFu's inspect_style, since the
|
||||
# class variable name is the same. Yay for namespace
|
||||
# pollution!
|
||||
def inspect_style=()
|
||||
PacketFu.inspect_style(arg)
|
||||
end
|
||||
|
||||
#method_missing() delegates protocol-specific field actions to the apporpraite
|
||||
#class variable (which contains the associated packet type)
|
||||
#This register-of-protocols style switch will work for the
|
||||
#forseeable future (there aren't /that/ many packet types), and it's a handy
|
||||
#way to know at a glance what packet types are supported.
|
||||
def method_missing(sym, *args, &block)
|
||||
case sym.to_s
|
||||
when /^is_([a-zA-Z0-9]+)\?/
|
||||
ptype = $1
|
||||
if PacketFu.packet_prefixes.index(ptype)
|
||||
self.send(:handle_is_identity, $1)
|
||||
else
|
||||
super
|
||||
end
|
||||
when /^([a-zA-Z0-9]+)_.+/
|
||||
ptype = $1
|
||||
if PacketFu.packet_prefixes.index(ptype)
|
||||
self.instance_variable_get("@#{ptype}_header").send(sym,*args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(sym, include_private = false)
|
||||
if sym.to_s =~ /^(invalid|eth|arp|ip|icmp|udp|hsrp|tcp|ipv6)_/
|
||||
self.instance_variable_get("@#{$1}_header").respond_to? sym
|
||||
elsif sym.to_s =~ /^is_([a-zA-Z0-9]+)\?/
|
||||
if PacketFu.packet_prefixes.index($1)
|
||||
true
|
||||
else
|
||||
super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
end # class Packet
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,611 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module StructFu
|
||||
|
||||
# Set the endianness for the various Int classes. Takes either :little or :big.
|
||||
def set_endianness(e=nil)
|
||||
unless [:little, :big].include? e
|
||||
raise ArgumentError, "Unknown endianness for #{self.class}"
|
||||
end
|
||||
@int32 = e == :little ? Int32le : Int32be
|
||||
@int16 = e == :little ? Int16le : Int16be
|
||||
return e
|
||||
end
|
||||
|
||||
# Instead of returning the "size" of the object, which is usually the
|
||||
# number of elements of the Struct, returns the size of the object after
|
||||
# a to_s. Essentially, a short version of self.to_size.size
|
||||
def sz
|
||||
self.to_s.size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module PacketFu
|
||||
|
||||
# PcapHeader represents the header portion of a libpcap file (the packets
|
||||
# themselves are in the PcapPackets array). See
|
||||
# http://wiki.wireshark.org/Development/LibpcapFileFormat for details.
|
||||
#
|
||||
# Depending on the endianness (set with :endian), elements are either
|
||||
# :little endian or :big endian.
|
||||
#
|
||||
# ==== PcapHeader Definition
|
||||
#
|
||||
# Symbol :endian Default: :little
|
||||
# Int32 :magic Default: 0xa1b2c3d4 # :big is 0xd4c3b2a1
|
||||
# Int16 :ver_major Default: 2
|
||||
# Int16 :ver_minor Default: 4
|
||||
# Int32 :thiszone
|
||||
# Int32 :sigfigs
|
||||
# Int32 :snaplen Default: 0xffff
|
||||
# Int32 :network Default: 1
|
||||
class PcapHeader < Struct.new(:endian, :magic, :ver_major, :ver_minor,
|
||||
:thiszone, :sigfigs, :snaplen, :network)
|
||||
include StructFu
|
||||
|
||||
MAGIC_INT32 = 0xa1b2c3d4
|
||||
MAGIC_LITTLE = [MAGIC_INT32].pack("V")
|
||||
MAGIC_BIG = [MAGIC_INT32].pack("N")
|
||||
|
||||
def initialize(args={})
|
||||
set_endianness(args[:endian] ||= :little)
|
||||
init_fields(args)
|
||||
super(args[:endian], args[:magic], args[:ver_major],
|
||||
args[:ver_minor], args[:thiszone], args[:sigfigs],
|
||||
args[:snaplen], args[:network])
|
||||
end
|
||||
|
||||
# Called by initialize to set the initial fields.
|
||||
def init_fields(args={})
|
||||
args[:magic] = @int32.new(args[:magic] || PcapHeader::MAGIC_INT32)
|
||||
args[:ver_major] = @int16.new(args[:ver_major] || 2)
|
||||
args[:ver_minor] ||= @int16.new(args[:ver_minor] || 4)
|
||||
args[:thiszone] ||= @int32.new(args[:thiszone])
|
||||
args[:sigfigs] ||= @int32.new(args[:sigfigs])
|
||||
args[:snaplen] ||= @int32.new(args[:snaplen] || 0xffff)
|
||||
args[:network] ||= @int32.new(args[:network] || 1)
|
||||
return args
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a[1,7].map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
# TODO: Need to test this by getting a hold of a big endian pcap file.
|
||||
# Conversion from big to little shouldn't be that big of a deal.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
str.force_encoding("binary") if str.respond_to? :force_encoding
|
||||
if str[0,4] == self[:magic].to_s
|
||||
self[:magic].read str[0,4]
|
||||
self[:ver_major].read str[4,2]
|
||||
self[:ver_minor].read str[6,2]
|
||||
self[:thiszone].read str[8,4]
|
||||
self[:sigfigs].read str[12,4]
|
||||
self[:snaplen].read str[16,4]
|
||||
self[:network].read str[20,4]
|
||||
else
|
||||
raise "Incorrect magic for libpcap"
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# The Timestamp class defines how Timestamps appear in libpcap files.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Symbol :endian Default: :little
|
||||
# Int32 :sec
|
||||
# Int32 :usec
|
||||
class Timestamp < Struct.new(:endian, :sec, :usec)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
set_endianness(args[:endian] ||= :little)
|
||||
init_fields(args)
|
||||
super(args[:endian], args[:sec], args[:usec])
|
||||
end
|
||||
|
||||
# Called by initialize to set the initial fields.
|
||||
def init_fields(args={})
|
||||
args[:sec] = @int32.new(args[:sec])
|
||||
args[:usec] = @int32.new(args[:usec])
|
||||
return args
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a[1,2].map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:sec].read str[0,4]
|
||||
self[:usec].read str[4,4]
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# PcapPacket defines how individual packets are stored in a libpcap-formatted
|
||||
# file.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Timestamp :timestamp
|
||||
# Int32 :incl_len
|
||||
# Int32 :orig_len
|
||||
# String :data
|
||||
class PcapPacket < Struct.new(:endian, :timestamp, :incl_len,
|
||||
:orig_len, :data)
|
||||
include StructFu
|
||||
def initialize(args={})
|
||||
set_endianness(args[:endian] ||= :little)
|
||||
init_fields(args)
|
||||
super(args[:endian], args[:timestamp], args[:incl_len],
|
||||
args[:orig_len], args[:data])
|
||||
end
|
||||
|
||||
# Called by initialize to set the initial fields.
|
||||
def init_fields(args={})
|
||||
args[:timestamp] = Timestamp.new(:endian => args[:endian]).read(args[:timestamp])
|
||||
args[:incl_len] = args[:incl_len].nil? ? @int32.new(args[:data].to_s.size) : @int32.new(args[:incl_len])
|
||||
args[:orig_len] = @int32.new(args[:orig_len])
|
||||
args[:data] = StructFu::String.new.read(args[:data])
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a[1,4].map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
return unless str
|
||||
force_binary(str)
|
||||
self[:timestamp].read str[0,8]
|
||||
self[:incl_len].read str[8,4]
|
||||
self[:orig_len].read str[12,4]
|
||||
self[:data].read str[16,self[:incl_len].to_i]
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# PcapPackets is a collection of PcapPacket objects.
|
||||
class PcapPackets < Array
|
||||
|
||||
include StructFu
|
||||
|
||||
attr_accessor :endian # probably ought to be read-only but who am i.
|
||||
|
||||
def initialize(args={})
|
||||
@endian = args[:endian] || :little
|
||||
end
|
||||
|
||||
def force_binary(str)
|
||||
str.force_encoding "binary" if str.respond_to? :force_encoding
|
||||
end
|
||||
|
||||
# Reads a string to populate the object. Note, this read takes in the
|
||||
# whole pcap file, since we need to see the magic to know what
|
||||
# endianness we're dealing with.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
if str[0,4] == PcapHeader::MAGIC_BIG
|
||||
@endian = :big
|
||||
elsif str[0,4] == PcapHeader::MAGIC_LITTLE
|
||||
@endian = :little
|
||||
else
|
||||
raise ArgumentError, "Unknown file format for #{self.class}"
|
||||
end
|
||||
body = str[24,str.size]
|
||||
while body.size > 16 # TODO: catch exceptions on malformed packets at end
|
||||
p = PcapPacket.new(:endian => @endian)
|
||||
p.read(body)
|
||||
self<<p
|
||||
body = body[p.sz,body.size]
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def to_s
|
||||
self.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# PcapFile is a complete libpcap file struct, made up of two elements, a
|
||||
# PcapHeader and PcapPackets.
|
||||
#
|
||||
# See http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
#
|
||||
# PcapFile also can behave as a singleton class, which is usually the better
|
||||
# way to handle pcap files of really any size, since it doesn't require
|
||||
# storing packets before handing them off to a given block. This is really
|
||||
# the way to go.
|
||||
class PcapFile < Struct.new(:endian, :head, :body)
|
||||
include StructFu
|
||||
|
||||
class << self
|
||||
|
||||
# Takes a given file and returns an array of the packet bytes. Here
|
||||
# for backwards compatibilty.
|
||||
def file_to_array(fname)
|
||||
PcapFile.new.file_to_array(:f => fname)
|
||||
end
|
||||
|
||||
# Takes a given file name, and reads out the packets. If given a block,
|
||||
# it will yield back a PcapPacket object per packet found.
|
||||
def read(fname,&block)
|
||||
file_header = PcapHeader.new
|
||||
pcap_packets = PcapPackets.new
|
||||
unless File.readable? fname
|
||||
raise ArgumentError, "Cannot read file `#{fname}'"
|
||||
end
|
||||
begin
|
||||
file_handle = File.open(fname, "rb")
|
||||
file_header.read file_handle.read(24)
|
||||
packet_count = 0
|
||||
pcap_packet = PcapPacket.new(:endian => file_header.endian)
|
||||
while pcap_packet.read file_handle.read(16) do
|
||||
len = pcap_packet.incl_len
|
||||
pcap_packet.data = StructFu::String.new.read(file_handle.read(len.to_i))
|
||||
packet_count += 1
|
||||
if pcap_packet.data.size < len.to_i
|
||||
warn "Packet ##{packet_count} is corrupted: expected #{len.to_i}, got #{pcap_packet.data.size}. Exiting."
|
||||
break
|
||||
end
|
||||
if block
|
||||
yield pcap_packet
|
||||
else
|
||||
pcap_packets << pcap_packet.clone
|
||||
end
|
||||
end
|
||||
ensure
|
||||
file_handle.close
|
||||
end
|
||||
block ? packet_count : pcap_packets
|
||||
end
|
||||
|
||||
# Takes a filename, and an optional block. If a block is given,
|
||||
# yield back the raw packet data from the given file. Otherwise,
|
||||
# return an array of parsed packets.
|
||||
def read_packet_bytes(fname,&block)
|
||||
count = 0
|
||||
packets = [] unless block
|
||||
read(fname) do |packet|
|
||||
if block
|
||||
count += 1
|
||||
yield packet.data.to_s
|
||||
else
|
||||
packets << packet.data.to_s
|
||||
end
|
||||
end
|
||||
block ? count : packets
|
||||
end
|
||||
|
||||
alias :file_to_array :read_packet_bytes
|
||||
|
||||
# Takes a filename, and an optional block. If a block is given,
|
||||
# yield back parsed packets from the given file. Otherwise, return
|
||||
# an array of parsed packets.
|
||||
#
|
||||
# This is a brazillian times faster than the old methods of extracting
|
||||
# packets from files.
|
||||
def read_packets(fname,&block)
|
||||
count = 0
|
||||
packets = [] unless block
|
||||
read_packet_bytes(fname) do |packet|
|
||||
if block
|
||||
count += 1
|
||||
yield Packet.parse(packet)
|
||||
else
|
||||
packets << Packet.parse(packet)
|
||||
end
|
||||
end
|
||||
block ? count : packets
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
init_fields(args)
|
||||
@filename = args.delete :filename
|
||||
super(args[:endian], args[:head], args[:body])
|
||||
end
|
||||
|
||||
# Called by initialize to set the initial fields.
|
||||
def init_fields(args={})
|
||||
args[:head] = PcapHeader.new(:endian => args[:endian]).read(args[:head])
|
||||
args[:body] = PcapPackets.new(:endian => args[:endian]).read(args[:body])
|
||||
return args
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self[:head].to_s + self[:body].map {|p| p.to_s}.join
|
||||
end
|
||||
|
||||
# Clears the contents of the PcapFile.
|
||||
def clear
|
||||
self[:body].clear
|
||||
end
|
||||
|
||||
# Reads a string to populate the object. Note that this appends new packets to
|
||||
# any existing packets in the PcapFile.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
self[:head].read str[0,24]
|
||||
self[:body].read str
|
||||
self
|
||||
end
|
||||
|
||||
# Clears the contents of the PcapFile prior to reading in a new string.
|
||||
def read!(str)
|
||||
clear
|
||||
force_binary(str)
|
||||
self.read str
|
||||
end
|
||||
|
||||
# A shorthand method for opening a file and reading in the packets. Note
|
||||
# that readfile clears any existing packets, since that seems to be the
|
||||
# typical use.
|
||||
def readfile(file)
|
||||
fdata = File.open(file, "rb") {|f| f.read}
|
||||
self.read! fdata
|
||||
end
|
||||
|
||||
# Calls the class method with this object's @filename
|
||||
def read_packet_bytes(fname=@filename,&block)
|
||||
raise ArgumentError, "Need a file" unless fname
|
||||
return self.class.read_packet_bytes(fname, &block)
|
||||
end
|
||||
|
||||
# Calls the class method with this object's @filename
|
||||
def read_packets(fname=@filename,&block)
|
||||
raise ArgumentError, "Need a file" unless fname
|
||||
return self.class.read_packets(fname, &block)
|
||||
end
|
||||
|
||||
# file_to_array() translates a libpcap file into an array of packets.
|
||||
# Note that this strips out pcap timestamps -- if you'd like to retain
|
||||
# timestamps and other libpcap file information, you will want to
|
||||
# use read() instead.
|
||||
def file_to_array(args={})
|
||||
filename = args[:filename] || args[:file] || args[:f]
|
||||
if filename
|
||||
self.read! File.open(filename, "rb") {|f| f.read}
|
||||
end
|
||||
if args[:keep_timestamps] || args[:keep_ts] || args[:ts]
|
||||
self[:body].map {|x| {x.timestamp.to_s => x.data.to_s} }
|
||||
else
|
||||
self[:body].map {|x| x.data.to_s}
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :f2a, :file_to_array
|
||||
|
||||
# Takes an array of packets (as generated by file_to_array), and writes them
|
||||
# to a file. Valid arguments are:
|
||||
#
|
||||
# :filename
|
||||
# :array # Can either be an array of packet data, or a hash-value pair of timestamp => data.
|
||||
# :timestamp # Sets an initial timestamp
|
||||
# :ts_inc # Sets the increment between timestamps. Defaults to 1 second.
|
||||
# :append # If true, then the packets are appended to the end of a file.
|
||||
def array_to_file(args={})
|
||||
if args.kind_of? Hash
|
||||
filename = args[:filename] || args[:file] || args[:f]
|
||||
arr = args[:array] || args[:arr] || args[:a]
|
||||
ts = args[:timestamp] || args[:ts] || Time.now.to_i
|
||||
ts_inc = args[:timestamp_increment] || args[:ts_inc] || 1
|
||||
append = !!args[:append]
|
||||
elsif args.kind_of? Array
|
||||
arr = args
|
||||
filename = append = nil
|
||||
else
|
||||
raise ArgumentError, "Unknown argument. Need either a Hash or Array."
|
||||
end
|
||||
unless arr.kind_of? Array
|
||||
raise ArgumentError, "Need an array to read packets from"
|
||||
end
|
||||
arr.each_with_index do |p,i|
|
||||
if p.kind_of? Hash # Binary timestamps are included
|
||||
this_ts = p.keys.first
|
||||
this_incl_len = p.values.first.size
|
||||
this_orig_len = this_incl_len
|
||||
this_data = p.values.first
|
||||
else # it's an array
|
||||
this_ts = Timestamp.new(:endian => self[:endian], :sec => ts + (ts_inc * i)).to_s
|
||||
this_incl_len = p.to_s.size
|
||||
this_orig_len = this_incl_len
|
||||
this_data = p.to_s
|
||||
end
|
||||
this_pkt = PcapPacket.new({:endian => self[:endian],
|
||||
:timestamp => this_ts,
|
||||
:incl_len => this_incl_len,
|
||||
:orig_len => this_orig_len,
|
||||
:data => this_data }
|
||||
)
|
||||
self[:body] << this_pkt
|
||||
end
|
||||
if filename
|
||||
self.to_f(:filename => filename, :append => append)
|
||||
else
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :a2f, :array_to_file
|
||||
|
||||
# Just like array_to_file, but clears any existing packets from the array first.
|
||||
def array_to_file!(arr)
|
||||
clear
|
||||
array_to_file(arr)
|
||||
end
|
||||
|
||||
alias_method :a2f!, :array_to_file!
|
||||
|
||||
# Writes the PcapFile to a file. Takes the following arguments:
|
||||
#
|
||||
# :filename # The file to write to.
|
||||
# :append # If set to true, the packets are appended to the file, rather than overwriting.
|
||||
def to_file(args={})
|
||||
filename = args[:filename] || args[:file] || args[:f]
|
||||
unless (!filename.nil? || filename.kind_of?(String))
|
||||
raise ArgumentError, "Need a :filename for #{self.class}"
|
||||
end
|
||||
append = args[:append]
|
||||
if append
|
||||
if File.exists? filename
|
||||
File.open(filename,'ab') {|file| file.write(self.body.to_s)}
|
||||
else
|
||||
File.open(filename,'wb') {|file| file.write(self.to_s)}
|
||||
end
|
||||
else
|
||||
File.open(filename,'wb') {|file| file.write(self.to_s)}
|
||||
end
|
||||
[filename, self.body.sz, self.body.size]
|
||||
end
|
||||
|
||||
alias_method :to_f, :to_file
|
||||
|
||||
# Shorthand method for writing to a file. Can take either :file => 'name.pcap' or
|
||||
# simply 'name.pcap'
|
||||
def write(filename='out.pcap')
|
||||
if filename.kind_of?(Hash)
|
||||
f = filename[:filename] || filename[:file] || filename[:f] || 'out.pcap'
|
||||
else
|
||||
f = filename.to_s
|
||||
end
|
||||
self.to_file(:filename => f.to_s, :append => false)
|
||||
end
|
||||
|
||||
# Shorthand method for appending to a file. Can take either :file => 'name.pcap' or
|
||||
# simply 'name.pcap'
|
||||
def append(filename='out.pcap')
|
||||
if filename.kind_of?(Hash)
|
||||
f = filename[:filename] || filename[:file] || filename[:f] || 'out.pcap'
|
||||
else
|
||||
f = filename.to_s
|
||||
end
|
||||
self.to_file(:filename => f, :append => true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module PacketFu
|
||||
|
||||
# Read is largely deprecated. It was current in PacketFu 0.2.0, but isn't all that useful
|
||||
# in 0.3.0 and beyond. Expect it to go away completely by version 1.0. So, the main use
|
||||
# of this class is to learn how to do exactly the same things using the PcapFile object.
|
||||
class Read
|
||||
|
||||
class << self
|
||||
|
||||
# Reads the magic string of a pcap file, and determines
|
||||
# if it's :little or :big endian.
|
||||
def get_byte_order(pcap_file)
|
||||
byte_order = ((pcap_file[0,4] == PcapHeader::MAGIC_LITTLE) ? :little : :big)
|
||||
return byte_order
|
||||
end
|
||||
|
||||
# set_byte_order is pretty much totally deprecated.
|
||||
def set_byte_order(byte_order)
|
||||
PacketFu.instance_variable_set(:@byte_order,byte_order)
|
||||
return true
|
||||
end
|
||||
|
||||
# A wrapper for PcapFile#file_to_array, but only returns the array. Actually
|
||||
# using the PcapFile object is going to be more useful.
|
||||
def file_to_array(args={})
|
||||
filename = args[:filename] || args[:file] || args[:out]
|
||||
raise ArgumentError, "Need a :filename in string form to read from." if (filename.nil? || filename.class != String)
|
||||
PcapFile.new.file_to_array(args)
|
||||
end
|
||||
|
||||
alias_method :f2a, :file_to_array
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module PacketFu
|
||||
|
||||
# Write is largely deprecated. It was current in PacketFu 0.2.0, but isn't all that useful
|
||||
# in 0.3.0 and beyond. Expect it to go away completely by version 1.0, as working with
|
||||
# PacketFu::PcapFile directly is generally going to be more rewarding.
|
||||
class Write
|
||||
|
||||
class << self
|
||||
|
||||
# format_packets: Pretty much totally deprecated.
|
||||
def format_packets(args={})
|
||||
arr = args[:arr] || args[:array] || []
|
||||
ts = args[:ts] || args[:timestamp] || Time.now.to_i
|
||||
ts_inc = args[:ts_inc] || args[:timestamp_increment]
|
||||
pkts = PcapFile.new.array_to_file(:endian => PacketFu.instance_variable_get(:@byte_order),
|
||||
:arr => arr,
|
||||
:ts => ts,
|
||||
:ts_inc => ts_inc)
|
||||
pkts.body
|
||||
end
|
||||
|
||||
# array_to_file is a largely deprecated function for writing arrays of pcaps to a file.
|
||||
# Use PcapFile#array_to_file instead.
|
||||
def array_to_file(args={})
|
||||
filename = args[:filename] || args[:file] || args[:out] || :nowrite
|
||||
arr = args[:arr] || args[:array] || []
|
||||
ts = args[:ts] || args[:timestamp] || args[:time_stamp] || Time.now.to_f
|
||||
ts_inc = args[:ts_inc] || args[:timestamp_increment] || args[:time_stamp_increment]
|
||||
byte_order = args[:byte_order] || args[:byteorder] || args[:endian] || args[:endianness] || :little
|
||||
append = args[:append]
|
||||
Read.set_byte_order(byte_order) if [:big, :little].include? byte_order
|
||||
pf = PcapFile.new
|
||||
pf.array_to_file(:endian => PacketFu.instance_variable_get(:@byte_order),
|
||||
:arr => arr,
|
||||
:ts => ts,
|
||||
:ts_inc => ts_inc)
|
||||
if filename && filename != :nowrite
|
||||
if append
|
||||
pf.append(filename)
|
||||
else
|
||||
pf.write(filename)
|
||||
end
|
||||
return [filename,pf.to_s.size,arr.size,ts,ts_inc]
|
||||
else
|
||||
return [nil,pf.to_s.size,arr.size,ts,ts_inc]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
alias_method :a2f, :array_to_file
|
||||
|
||||
# Shorthand method for appending to a file. Also shouldn't use.
|
||||
def append(args={})
|
||||
array_to_file(args.merge(:append => true))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,269 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# ARPHeader is a complete ARP struct, used in ARPPacket.
|
||||
#
|
||||
# ARP is used to discover the machine address of nearby devices.
|
||||
#
|
||||
# See http://www.networksorcery.com/enp/protocol/arp.htm for details.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Int16 :arp_hw Default: 1 # Ethernet
|
||||
# Int16 :arp_proto, Default: 0x8000 # IP
|
||||
# Int8 :arp_hw_len, Default: 6
|
||||
# Int8 :arp_proto_len, Default: 4
|
||||
# Int16 :arp_opcode, Default: 1 # 1: Request, 2: Reply, 3: Request-Reverse, 4: Reply-Reverse
|
||||
# EthMac :arp_src_mac # From eth.rb
|
||||
# Octets :arp_src_ip # From ip.rb
|
||||
# EthMac :arp_dst_mac # From eth.rb
|
||||
# Octets :arp_dst_ip # From ip.rb
|
||||
# String :body
|
||||
class ARPHeader < Struct.new(:arp_hw, :arp_proto, :arp_hw_len,
|
||||
:arp_proto_len, :arp_opcode,
|
||||
:arp_src_mac, :arp_src_ip,
|
||||
:arp_dst_mac, :arp_dst_ip,
|
||||
:body)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
src_mac = args[:arp_src_mac] || (args[:config][:eth_src] if args[:config])
|
||||
src_ip_bin = args[:arp_src_ip] || (args[:config][:ip_src_bin] if args[:config])
|
||||
|
||||
super(
|
||||
Int16.new(args[:arp_hw] || 1),
|
||||
Int16.new(args[:arp_proto] ||0x0800),
|
||||
Int8.new(args[:arp_hw_len] || 6),
|
||||
Int8.new(args[:arp_proto_len] || 4),
|
||||
Int16.new(args[:arp_opcode] || 1),
|
||||
EthMac.new.read(src_mac),
|
||||
Octets.new.read(src_ip_bin),
|
||||
EthMac.new.read(args[:arp_dst_mac]),
|
||||
Octets.new.read(args[:arp_dst_ip]),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:arp_hw].read(str[0,2])
|
||||
self[:arp_proto].read(str[2,2])
|
||||
self[:arp_hw_len].read(str[4,1])
|
||||
self[:arp_proto_len].read(str[5,1])
|
||||
self[:arp_opcode].read(str[6,2])
|
||||
self[:arp_src_mac].read(str[8,6])
|
||||
self[:arp_src_ip].read(str[14,4])
|
||||
self[:arp_dst_mac].read(str[18,6])
|
||||
self[:arp_dst_ip].read(str[24,4])
|
||||
self[:body].read(str[28,str.size])
|
||||
self
|
||||
end
|
||||
|
||||
# Setter for the ARP hardware type.
|
||||
def arp_hw=(i); typecast i; end
|
||||
# Getter for the ARP hardware type.
|
||||
def arp_hw; self[:arp_hw].to_i; end
|
||||
# Setter for the ARP protocol.
|
||||
def arp_proto=(i); typecast i; end
|
||||
# Getter for the ARP protocol.
|
||||
def arp_proto; self[:arp_proto].to_i; end
|
||||
# Setter for the ARP hardware type length.
|
||||
def arp_hw_len=(i); typecast i; end
|
||||
# Getter for the ARP hardware type length.
|
||||
def arp_hw_len; self[:arp_hw_len].to_i; end
|
||||
# Setter for the ARP protocol length.
|
||||
def arp_proto_len=(i); typecast i; end
|
||||
# Getter for the ARP protocol length.
|
||||
def arp_proto_len; self[:arp_proto_len].to_i; end
|
||||
# Setter for the ARP opcode.
|
||||
def arp_opcode=(i); typecast i; end
|
||||
# Getter for the ARP opcode.
|
||||
def arp_opcode; self[:arp_opcode].to_i; end
|
||||
# Setter for the ARP source MAC address.
|
||||
def arp_src_mac=(i); typecast i; end
|
||||
# Getter for the ARP source MAC address.
|
||||
def arp_src_mac; self[:arp_src_mac].to_s; end
|
||||
# Getter for the ARP source IP address.
|
||||
def arp_src_ip=(i); typecast i; end
|
||||
# Setter for the ARP source IP address.
|
||||
def arp_src_ip; self[:arp_src_ip].to_s; end
|
||||
# Setter for the ARP destination MAC address.
|
||||
def arp_dst_mac=(i); typecast i; end
|
||||
# Setter for the ARP destination MAC address.
|
||||
def arp_dst_mac; self[:arp_dst_mac].to_s; end
|
||||
# Setter for the ARP destination IP address.
|
||||
def arp_dst_ip=(i); typecast i; end
|
||||
# Getter for the ARP destination IP address.
|
||||
def arp_dst_ip; self[:arp_dst_ip].to_s; end
|
||||
|
||||
# Set the source MAC address in a more readable way.
|
||||
def arp_saddr_mac=(mac)
|
||||
mac = EthHeader.mac2str(mac)
|
||||
self[:arp_src_mac].read(mac)
|
||||
self.arp_src_mac
|
||||
end
|
||||
|
||||
# Get a more readable source MAC address.
|
||||
def arp_saddr_mac
|
||||
EthHeader.str2mac(self[:arp_src_mac].to_s)
|
||||
end
|
||||
|
||||
# Set the destination MAC address in a more readable way.
|
||||
def arp_daddr_mac=(mac)
|
||||
mac = EthHeader.mac2str(mac)
|
||||
self[:arp_dst_mac].read(mac)
|
||||
self.arp_dst_mac
|
||||
end
|
||||
|
||||
# Get a more readable source MAC address.
|
||||
def arp_daddr_mac
|
||||
EthHeader.str2mac(self[:arp_dst_mac].to_s)
|
||||
end
|
||||
|
||||
# Set a more readable source IP address.
|
||||
def arp_saddr_ip=(addr)
|
||||
self[:arp_src_ip].read_quad(addr)
|
||||
end
|
||||
|
||||
# Get a more readable source IP address.
|
||||
def arp_saddr_ip
|
||||
self[:arp_src_ip].to_x
|
||||
end
|
||||
|
||||
# Set a more readable destination IP address.
|
||||
def arp_daddr_ip=(addr)
|
||||
self[:arp_dst_ip].read_quad(addr)
|
||||
end
|
||||
|
||||
# Get a more readable destination IP address.
|
||||
def arp_daddr_ip
|
||||
self[:arp_dst_ip].to_x
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
alias :arp_src_mac_readable :arp_saddr_mac
|
||||
alias :arp_dst_mac_readable :arp_daddr_mac
|
||||
alias :arp_src_ip_readable :arp_saddr_ip
|
||||
alias :arp_dst_ip_readable :arp_daddr_ip
|
||||
|
||||
def arp_proto_readable
|
||||
"0x%04x" % arp_proto
|
||||
end
|
||||
|
||||
end # class ARPHeader
|
||||
|
||||
# ARPPacket is used to construct ARP packets. They contain an EthHeader and an ARPHeader.
|
||||
# == Example
|
||||
#
|
||||
# require 'packetfu'
|
||||
# arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows")
|
||||
# arp_pkt.arp_saddr_mac="00:1c:23:44:55:66" # Your hardware address
|
||||
# arp_pkt.arp_saddr_ip="10.10.10.17" # Your IP address
|
||||
# arp_pkt.arp_daddr_ip="10.10.10.1" # Target IP address
|
||||
# arp_pkt.arp_opcode=1 # Request
|
||||
#
|
||||
# arp_pkt.to_w('eth0') # Inject on the wire. (requires root)
|
||||
# arp_pkt.to_f('/tmp/arp.pcap') # Write to a file.
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# :flavor
|
||||
# Sets the "flavor" of the ARP packet. Choices are currently:
|
||||
# :windows, :linux, :hp_deskjet
|
||||
# :eth
|
||||
# A pre-generated EthHeader object. If not specified, a new one will be created.
|
||||
# :arp
|
||||
# A pre-generated ARPHeader object. If not specificed, a new one will be created.
|
||||
# :config
|
||||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class ARPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :arp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless str.size >= 28
|
||||
return false unless str[12,2] == "\x08\x06"
|
||||
true
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@arp_header.read(str[14,str.size])
|
||||
@eth_header.body = @arp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@arp_header = ARPHeader.new(args).read(args[:arp])
|
||||
@eth_header.eth_proto = "\x08\x06"
|
||||
@eth_header.body=@arp_header
|
||||
|
||||
# Please send more flavors to todb+packetfu@planb-security.net.
|
||||
# Most of these initial fingerprints come from one (1) sample.
|
||||
case (args[:flavor].nil?) ? :nil : args[:flavor].to_s.downcase.intern
|
||||
when :windows; @arp_header.body = "\x00" * 64 # 64 bytes of padding
|
||||
when :linux; @arp_header.body = "\x00" * 4 + # 32 bytes of padding
|
||||
"\x00\x07\x5c\x14" + "\x00" * 4 +
|
||||
"\x00\x0f\x83\x34" + "\x00\x0f\x83\x74" +
|
||||
"\x01\x11\x83\x78" + "\x00\x00\x00\x0c" +
|
||||
"\x00\x00\x00\x00"
|
||||
when :hp_deskjet; # Pads up to 60 bytes.
|
||||
@arp_header.body = "\xe0\x90\x0d\x6c" +
|
||||
"\xff\xff\xee\xee" + "\x00" * 4 +
|
||||
"\xe0\x8f\xfa\x18\x00\x20"
|
||||
else; @arp_header.body = "\x00" * 18 # Pads up to 60 bytes.
|
||||
end
|
||||
|
||||
@headers = [@eth_header, @arp_header]
|
||||
super
|
||||
end
|
||||
|
||||
# Generates summary data for ARP packets.
|
||||
def peek_format
|
||||
peek_data = ["A "]
|
||||
peek_data << "%-5d" % self.to_s.size
|
||||
peek_data << arp_saddr_mac
|
||||
peek_data << "(#{arp_saddr_ip})"
|
||||
peek_data << "->"
|
||||
peek_data << case arp_daddr_mac
|
||||
when "00:00:00:00:00:00"; "Bcast00"
|
||||
when "ff:ff:ff:ff:ff:ff"; "BcastFF"
|
||||
else; arp_daddr_mac
|
||||
end
|
||||
peek_data << "(#{arp_daddr_ip})"
|
||||
peek_data << ":"
|
||||
peek_data << case arp_opcode
|
||||
when 1; "Requ"
|
||||
when 2; "Repl"
|
||||
when 3; "RReq"
|
||||
when 4; "RRpl"
|
||||
when 5; "IReq"
|
||||
when 6; "IRpl"
|
||||
else; "0x%02x" % arp_opcode
|
||||
end
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
# While there are lengths in ARPPackets, there's not
|
||||
# much to do with them.
|
||||
def recalc(args={})
|
||||
@headers[0].inspect
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,297 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# EthOui is the Organizationally Unique Identifier portion of a MAC address, used in EthHeader.
|
||||
#
|
||||
# See the OUI list at http://standards.ieee.org/regauth/oui/oui.txt
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Fixnum :b0
|
||||
# Fixnum :b1
|
||||
# Fixnum :b2
|
||||
# Fixnum :b3
|
||||
# Fixnum :b4
|
||||
# Fixnum :b5
|
||||
# Fixnum :local
|
||||
# Fixnum :multicast
|
||||
# Int16 :oui, Default: 0x1ac5 :)
|
||||
class EthOui < Struct.new(:b5, :b4, :b3, :b2, :b1, :b0, :local, :multicast, :oui)
|
||||
|
||||
# EthOui is unusual in that the bit values do not enjoy StructFu typing.
|
||||
def initialize(args={})
|
||||
args[:local] ||= 0
|
||||
args[:oui] ||= 0x1ac # :)
|
||||
args.each_pair {|k,v| args[k] = 0 unless v}
|
||||
super(args[:b5], args[:b4], args[:b3], args[:b2],
|
||||
args[:b1], args[:b0], args[:local], args[:multicast],
|
||||
args[:oui])
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
byte = 0
|
||||
byte += 0b10000000 if b5.to_i == 1
|
||||
byte += 0b01000000 if b4.to_i == 1
|
||||
byte += 0b00100000 if b3.to_i == 1
|
||||
byte += 0b00010000 if b2.to_i == 1
|
||||
byte += 0b00001000 if b1.to_i == 1
|
||||
byte += 0b00000100 if b0.to_i == 1
|
||||
byte += 0b00000010 if local.to_i == 1
|
||||
byte += 0b00000001 if multicast.to_i == 1
|
||||
[byte,oui].pack("Cn")
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
if 1.respond_to? :ord
|
||||
byte = str[0].ord
|
||||
else
|
||||
byte = str[0]
|
||||
end
|
||||
self[:b5] = byte & 0b10000000 == 0b10000000 ? 1 : 0
|
||||
self[:b4] = byte & 0b01000000 == 0b01000000 ? 1 : 0
|
||||
self[:b3] = byte & 0b00100000 == 0b00100000 ? 1 : 0
|
||||
self[:b2] = byte & 0b00010000 == 0b00010000 ? 1 : 0
|
||||
self[:b1] = byte & 0b00001000 == 0b00001000 ? 1 : 0
|
||||
self[:b0] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
||||
self[:local] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
||||
self[:multicast] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
||||
self[:oui] = str[1,2].unpack("n").first
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# EthNic is the Network Interface Controler portion of a MAC address, used in EthHeader.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Fixnum :n1
|
||||
# Fixnum :n2
|
||||
# Fixnum :n3
|
||||
#
|
||||
class EthNic < Struct.new(:n0, :n1, :n2)
|
||||
|
||||
# EthNic does not enjoy StructFu typing.
|
||||
def initialize(args={})
|
||||
args.each_pair {|k,v| args[k] = 0 unless v}
|
||||
super(args[:n0], args[:n1], args[:n2])
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
[n0,n1,n2].map {|x| x.to_i}.pack("C3")
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:n0], self[:n1], self[:n2] = str[0,3].unpack("C3")
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# EthMac is the combination of an EthOui and EthNic, used in EthHeader.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# EthOui :oui # See EthOui
|
||||
# EthNic :nic # See EthNic
|
||||
class EthMac < Struct.new(:oui, :nic)
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
EthOui.new.read(args[:oui]),
|
||||
EthNic.new.read(args[:nic]))
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
"#{self[:oui]}#{self[:nic]}"
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self.oui.read str[0,3]
|
||||
self.nic.read str[3,3]
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# EthHeader is a complete Ethernet struct, used in EthPacket.
|
||||
# It's the base header for all other protocols, such as IPHeader,
|
||||
# TCPHeader, etc.
|
||||
#
|
||||
# For more on the construction on MAC addresses, see
|
||||
# http://en.wikipedia.org/wiki/MAC_address
|
||||
#
|
||||
# TODO: Need to come up with a good way of dealing with vlan
|
||||
# tagging. Having a usually empty struct member seems weird,
|
||||
# but there may not be another way to do it if I want to preserve
|
||||
# the Eth-ness of vlan-tagged 802.1Q packets. Also, may as well
|
||||
# deal with 0x88a8 as well (http://en.wikipedia.org/wiki/802.1ad)
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# EthMac :eth_dst # See EthMac
|
||||
# EthMac :eth_src # See EthMac
|
||||
# Int16 :eth_proto, Default: 0x8000 # IP 0x0800, Arp 0x0806
|
||||
# String :body
|
||||
class EthHeader < Struct.new(:eth_dst, :eth_src, :eth_proto, :body)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
EthMac.new.read(args[:eth_dst]),
|
||||
EthMac.new.read(args[:eth_src]),
|
||||
Int16.new(args[:eth_proto] || 0x0800),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Setter for the Ethernet destination address.
|
||||
def eth_dst=(i); typecast(i); end
|
||||
# Getter for the Ethernet destination address.
|
||||
def eth_dst; self[:eth_dst].to_s; end
|
||||
# Setter for the Ethernet source address.
|
||||
def eth_src=(i); typecast(i); end
|
||||
# Getter for the Ethernet source address.
|
||||
def eth_src; self[:eth_src].to_s; end
|
||||
# Setter for the Ethernet protocol number.
|
||||
def eth_proto=(i); typecast(i); end
|
||||
# Getter for the Ethernet protocol number.
|
||||
def eth_proto; self[:eth_proto].to_i; end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:eth_dst].read str[0,6]
|
||||
self[:eth_src].read str[6,6]
|
||||
self[:eth_proto].read str[12,2]
|
||||
self[:body].read str[14,str.size]
|
||||
self
|
||||
end
|
||||
|
||||
# Converts a readable MAC (11:22:33:44:55:66) to a binary string.
|
||||
# Readable MAC's may be split on colons, dots, spaces, or underscores.
|
||||
#
|
||||
# irb> PacketFu::EthHeader.mac2str("11:22:33:44:55:66")
|
||||
#
|
||||
# #=> "\021\"3DUf"
|
||||
def self.mac2str(mac)
|
||||
if mac.split(/[:\x2d\x2e\x5f]+/).size == 6
|
||||
ret = mac.split(/[:\x2d\x2e\x20\x5f]+/).collect {|x| x.to_i(16)}.pack("C6")
|
||||
else
|
||||
raise ArgumentError, "Unkown format for mac address."
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
# Converts a binary string to a readable MAC (11:22:33:44:55:66).
|
||||
#
|
||||
# irb> PacketFu::EthHeader.str2mac("\x11\x22\x33\x44\x55\x66")
|
||||
#
|
||||
# #=> "11:22:33:44:55:66"
|
||||
def self.str2mac(mac='')
|
||||
if mac.to_s.size == 6 && mac.kind_of?(::String)
|
||||
ret = mac.unpack("C6").map {|x| sprintf("%02x",x)}.join(":")
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the source MAC address in a more readable way.
|
||||
def eth_saddr=(mac)
|
||||
mac = EthHeader.mac2str(mac)
|
||||
self[:eth_src].read mac
|
||||
self[:eth_src]
|
||||
end
|
||||
|
||||
# Gets the source MAC address in a more readable way.
|
||||
def eth_saddr
|
||||
EthHeader.str2mac(self[:eth_src].to_s)
|
||||
end
|
||||
|
||||
# Set the destination MAC address in a more readable way.
|
||||
def eth_daddr=(mac)
|
||||
mac = EthHeader.mac2str(mac)
|
||||
self[:eth_dst].read mac
|
||||
self[:eth_dst]
|
||||
end
|
||||
|
||||
# Gets the destination MAC address in a more readable way.
|
||||
def eth_daddr
|
||||
EthHeader.str2mac(self[:eth_dst].to_s)
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
alias :eth_dst_readable :eth_daddr
|
||||
alias :eth_src_readable :eth_saddr
|
||||
|
||||
def eth_proto_readable
|
||||
"0x%04x" % eth_proto
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# EthPacket is used to construct Ethernet packets. They contain an
|
||||
# Ethernet header, and that's about it.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# require 'packetfu'
|
||||
# eth_pkt = PacketFu::EthPacket.new
|
||||
# eth_pkt.eth_saddr="00:1c:23:44:55:66"
|
||||
# eth_pkt.eth_daddr="00:1c:24:aa:bb:cc"
|
||||
#
|
||||
# eth_pkt.to_w('eth0') # Inject on the wire. (require root)
|
||||
#
|
||||
class EthPacket < Packet
|
||||
attr_accessor :eth_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
# XXX Temporary fix. Need to extend the EthHeader class to handle more.
|
||||
valid_eth_types = [0x0800, 0x0806, 0x86dd]
|
||||
return false unless str.size >= 14
|
||||
type = str[12,2].unpack("n").first rescue nil
|
||||
return false unless valid_eth_types.include? type
|
||||
true
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
super(args)
|
||||
return self
|
||||
end
|
||||
|
||||
# Does nothing, really, since there's no length or
|
||||
# checksum to calculate for a straight Ethernet packet.
|
||||
def recalc(args={})
|
||||
@headers[0].inspect
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@headers = [@eth_header]
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,207 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# HSRPHeader is a complete HSRP struct, used in HSRPPacket. HSRP is typically used for
|
||||
# fault-tolerant default gateway in IP routing environment.
|
||||
#
|
||||
# For more on HSRP packets, see http://www.networksorcery.com/enp/protocol/hsrp.htm
|
||||
#
|
||||
# Submitted by fropert@packetfault.org. Thanks, Francois!
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Int8 :hsrp_version Default: 0 # Version
|
||||
# Int8 :hsrp_opcode # Opcode
|
||||
# Int8 :hsrp_state # State
|
||||
# Int8 :hsrp_hellotime Default: 3 # Hello Time
|
||||
# Int8 :hsrp_holdtime Default: 10 # Hold Time
|
||||
# Int8 :hsrp_priority # Priority
|
||||
# Int8 :hsrp_group # Group
|
||||
# Int8 :hsrp_reserved Default: 0 # Reserved
|
||||
# String :hsrp_password # Authentication Data
|
||||
# Octets :hsrp_vip # Virtual IP Address
|
||||
# String :body
|
||||
class HSRPHeader < Struct.new(:hsrp_version, :hsrp_opcode, :hsrp_state,
|
||||
:hsrp_hellotime, :hsrp_holdtime,
|
||||
:hsrp_priority, :hsrp_group,
|
||||
:hsrp_reserved, :hsrp_password,
|
||||
:hsrp_vip, :body)
|
||||
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
Int8.new(args[:hsrp_version] || 0),
|
||||
Int8.new(args[:hsrp_opcode]),
|
||||
Int8.new(args[:hsrp_state]),
|
||||
Int8.new(args[:hsrp_hellotime] || 3),
|
||||
Int8.new(args[:hsrp_holdtime] || 10),
|
||||
Int8.new(args[:hsrp_priority]),
|
||||
Int8.new(args[:hsrp_group]),
|
||||
Int8.new(args[:hsrp_reserved] || 0),
|
||||
StructFu::String.new.read(args[:hsrp_password] || "cisco\x00\x00\x00"),
|
||||
Octets.new.read(args[:hsrp_vip] || ("\x00" * 4)),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:hsrp_version].read(str[0,1])
|
||||
self[:hsrp_opcode].read(str[1,1])
|
||||
self[:hsrp_state].read(str[2,1])
|
||||
self[:hsrp_hellotime].read(str[3,1])
|
||||
self[:hsrp_holdtime].read(str[4,1])
|
||||
self[:hsrp_priority].read(str[5,1])
|
||||
self[:hsrp_group].read(str[6,1])
|
||||
self[:hsrp_reserved].read(str[7,1])
|
||||
self[:hsrp_password].read(str[8,8])
|
||||
self[:hsrp_vip].read(str[16,4])
|
||||
self[:body].read(str[20,str.size]) if str.size > 20
|
||||
self
|
||||
end
|
||||
|
||||
# Setter for the type.
|
||||
def hsrp_version=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_version; self[:hsrp_version].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_opcode=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_opcode; self[:hsrp_opcode].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_state=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_state; self[:hsrp_state].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_hellotime=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_hellotime; self[:hsrp_hellotime].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_holdtime=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_holdtime; self[:hsrp_holdtime].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_priority=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_priority; self[:hsrp_priority].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_group=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_group; self[:hsrp_group].to_i; end
|
||||
# Setter for the type.
|
||||
def hsrp_reserved=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def hsrp_reserved; self[:hsrp_reserved].to_i; end
|
||||
|
||||
def hsrp_addr=(addr)
|
||||
self[:hsrp_vip].read_quad(addr)
|
||||
end
|
||||
|
||||
# Returns a more readable IP source address.
|
||||
def hsrp_addr
|
||||
self[:hsrp_vip].to_x
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
alias :hsrp_vip_readable :hsrp_addr
|
||||
|
||||
def hsrp_password_readable
|
||||
hsrp_password.to_s.inspect
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# HSRPPacket is used to construct HSRP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# hsrp_pkt.new
|
||||
# hsrp_pkt.hsrp_opcode = 0
|
||||
# hsrp_pkt.hsrp_state = 16
|
||||
# hsrp_pkt.hsrp_priority = 254
|
||||
# hsrp_pkt.hsrp_group = 1
|
||||
# hsrp_pkt.hsrp_vip = 10.100.100.254
|
||||
# hsrp_pkt.recalc
|
||||
# hsrp_pkt.to_f('/tmp/hsrp.pcap')
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# :eth
|
||||
# A pre-generated EthHeader object.
|
||||
# :ip
|
||||
# A pre-generated IPHeader object.
|
||||
# :udp
|
||||
# A pre-generated UDPHeader object.
|
||||
# :flavor
|
||||
# TODO: HSRP packets don't tend have any flavor.
|
||||
# :config
|
||||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class HSRPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :udp_header, :hsrp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 54
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless UDPPacket.can_parse? str
|
||||
temp_packet = UDPPacket.new
|
||||
temp_packet.read(str)
|
||||
if temp_packet.ip_ttl == 1 and [temp_packet.udp_sport,temp_packet.udp_dport] == [1985,1985]
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
@udp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
@ip_header.body = @udp_header
|
||||
@hsrp_header.read(str[14+(@ip_header.ip_hlen)+8,str.size])
|
||||
@udp_header.body = @hsrp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
||||
@ip_header.ip_proto = 0x11
|
||||
@udp_header = UDPHeader.new(args).read(args[:udp])
|
||||
@hsrp_header = HSRPHeader.new(args).read(args[:hsrp])
|
||||
@udp_header.body = @hsrp_header
|
||||
@ip_header.body = @udp_header
|
||||
@eth_header.body = @ip_header
|
||||
@headers = [@eth_header, @ip_header, @udp_header, @hsrp_header]
|
||||
super
|
||||
end
|
||||
|
||||
# Peek provides summary data on packet contents.
|
||||
def peek_format
|
||||
peek_data = ["UH "]
|
||||
peek_data << "%-5d" % self.to_s.size
|
||||
peek_data << "%-16s" % self.hsrp_addr
|
||||
peek_data << "%-4d" % self.hsrp_group
|
||||
peek_data << "%-35s" % self.hsrp_password_readable
|
||||
peek_data << "%-15s" % self.ip_saddr
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,180 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# ICMPHeader is a complete ICMP struct, used in ICMPPacket. ICMP is
|
||||
# typically used for network administration and connectivity testing.
|
||||
#
|
||||
# For more on ICMP packets, see
|
||||
# http://www.networksorcery.com/enp/protocol/icmp.htm
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Int8 :icmp_type # Type
|
||||
# Int8 :icmp_code # Code
|
||||
# Int16 :icmp_sum Default: calculated # Checksum
|
||||
# String :body
|
||||
class ICMPHeader < Struct.new(:icmp_type, :icmp_code, :icmp_sum, :body)
|
||||
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
Int8.new(args[:icmp_type]),
|
||||
Int8.new(args[:icmp_code]),
|
||||
Int16.new(args[:icmp_sum] || icmp_calc_sum),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:icmp_type].read(str[0,1])
|
||||
self[:icmp_code].read(str[1,1])
|
||||
self[:icmp_sum].read(str[2,2])
|
||||
self[:body].read(str[4,str.size])
|
||||
self
|
||||
end
|
||||
|
||||
# Setter for the type.
|
||||
def icmp_type=(i); typecast i; end
|
||||
# Getter for the type.
|
||||
def icmp_type; self[:icmp_type].to_i; end
|
||||
# Setter for the code.
|
||||
def icmp_code=(i); typecast i; end
|
||||
# Getter for the code.
|
||||
def icmp_code; self[:icmp_code].to_i; end
|
||||
# Setter for the checksum. Note, this is calculated automatically with
|
||||
# icmp_calc_sum.
|
||||
def icmp_sum=(i); typecast i; end
|
||||
# Getter for the checksum.
|
||||
def icmp_sum; self[:icmp_sum].to_i; end
|
||||
|
||||
# Calculates and sets the checksum for the object.
|
||||
def icmp_calc_sum
|
||||
checksum = (icmp_type.to_i << 8) + icmp_code.to_i
|
||||
chk_body = (body.to_s.size % 2 == 0 ? body.to_s : body.to_s + "\x00")
|
||||
if 1.respond_to? :ord
|
||||
chk_body.scan(/../).map { |x| (x[0].ord << 8) + x[1].ord }.each { |y| checksum += y }
|
||||
else
|
||||
chk_body.scan(/../).map { |x| (x[0] << 8) + x[1] }.each { |y| checksum += y }
|
||||
end
|
||||
checksum = checksum % 0xffff
|
||||
checksum = 0xffff - checksum
|
||||
checksum == 0 ? 0xffff : checksum
|
||||
end
|
||||
|
||||
# Recalculates the calculatable fields for ICMP.
|
||||
def icmp_recalc(arg=:all)
|
||||
# How silly is this, you can't intern a symbol in ruby 1.8.7pl72?
|
||||
# I'm this close to monkey patching Symbol so you can force it...
|
||||
arg = arg.intern if arg.respond_to? :intern
|
||||
case arg
|
||||
when :icmp_sum
|
||||
self.icmp_sum=icmp_calc_sum
|
||||
when :all
|
||||
self.icmp_sum=icmp_calc_sum
|
||||
else
|
||||
raise ArgumentError, "No such field `#{arg}'"
|
||||
end
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
def icmp_sum_readable
|
||||
"0x%04x" % icmp_sum
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# ICMPPacket is used to construct ICMP Packets. They contain an EthHeader, an IPHeader, and a ICMPHeader.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# icmp_pkt.new
|
||||
# icmp_pkt.icmp_type = 8
|
||||
# icmp_pkt.icmp_code = 0
|
||||
# icmp_pkt.payload = "ABC, easy as 123. As simple as do-re-mi. ABC, 123, baby, you and me!"
|
||||
#
|
||||
# icmp_pkt.ip_saddr="1.2.3.4"
|
||||
# icmp_pkt.ip_daddr="5.6.7.8"
|
||||
#
|
||||
# icmp_pkt.recalc
|
||||
# icmp_pkt.to_f('/tmp/icmp.pcap')
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# :eth
|
||||
# A pre-generated EthHeader object.
|
||||
# :ip
|
||||
# A pre-generated IPHeader object.
|
||||
# :flavor
|
||||
# TODO: Sets the "flavor" of the ICMP packet. Pings, in particular, often betray their true
|
||||
# OS.
|
||||
# :config
|
||||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class ICMPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :icmp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 38
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless str[23,1] == "\x01"
|
||||
return true
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
@icmp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
@ip_header.body = @icmp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
||||
@ip_header.ip_proto = 1
|
||||
@icmp_header = ICMPHeader.new(args).read(args[:icmp])
|
||||
|
||||
@ip_header.body = @icmp_header
|
||||
@eth_header.body = @ip_header
|
||||
|
||||
@headers = [@eth_header, @ip_header, @icmp_header]
|
||||
super
|
||||
end
|
||||
|
||||
# Peek provides summary data on packet contents.
|
||||
def peek_format
|
||||
peek_data = ["IC "] # I is taken by IP
|
||||
peek_data << "%-5d" % self.to_s.size
|
||||
type = case self.icmp_type.to_i
|
||||
when 8
|
||||
"ping"
|
||||
when 0
|
||||
"pong"
|
||||
else
|
||||
"%02x-%02x" % [self.icmp_type, self.icmp_code]
|
||||
end
|
||||
peek_data << "%-21s" % "#{self.ip_saddr}:#{type}"
|
||||
peek_data << "->"
|
||||
peek_data << "%21s" % "#{self.ip_daddr}"
|
||||
peek_data << "%23s" % "I:"
|
||||
peek_data << "%04x" % self.ip_id
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,56 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# InvalidHeader catches all packets that we don't already have a Struct for,
|
||||
# or for whatever reason, violates some basic packet rules for other packet
|
||||
# types.
|
||||
class InvalidHeader < Struct.new(:body)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
args[:body] ||= StructFu::String.new
|
||||
super(args[:body])
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:body].read str
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# You probably don't want to write invalid packets on purpose.
|
||||
class InvalidPacket < Packet
|
||||
attr_accessor :invalid_header
|
||||
|
||||
# Any packet is potentially an invalid packet
|
||||
def self.can_parse?(str)
|
||||
true
|
||||
end
|
||||
|
||||
def self.layer
|
||||
0
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
@invalid_header.read(str)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@invalid_header = (args[:invalid] || InvalidHeader.new)
|
||||
@headers = [@invalid_header]
|
||||
end
|
||||
end
|
||||
|
||||
end # module PacketFu
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,379 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'ipaddr'
|
||||
module PacketFu
|
||||
|
||||
# Octets implements the addressing scheme for IP.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Int8 :o1
|
||||
# Int8 :o2
|
||||
# Int8 :o3
|
||||
# Int8 :o4
|
||||
class Octets < Struct.new(:o1, :o2, :o3, :o4)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
Int8.new(args[:o1]),
|
||||
Int8.new(args[:o2]),
|
||||
Int8.new(args[:o3]),
|
||||
Int8.new(args[:o4]))
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:o1].read str[0,1]
|
||||
self[:o2].read str[1,1]
|
||||
self[:o3].read str[2,1]
|
||||
self[:o4].read str[3,1]
|
||||
self
|
||||
end
|
||||
|
||||
# Returns an address in dotted-quad format.
|
||||
def to_x
|
||||
ip_str = [o1, o2, o3, o4].map {|x| x.to_i.to_s}.join('.')
|
||||
IPAddr.new(ip_str).to_s
|
||||
end
|
||||
|
||||
# Returns an address in numerical format.
|
||||
def to_i
|
||||
ip_str = [o1, o2, o3, o4].map {|x| x.to_i.to_s}.join('.')
|
||||
IPAddr.new(ip_str).to_i
|
||||
end
|
||||
|
||||
# Set the IP Address by reading a dotted-quad address.
|
||||
def read_quad(str)
|
||||
read([IPAddr.new(str).to_i].pack("N"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# IPHeader is a complete IP struct, used in IPPacket. Most traffic on most networks today is IP-based.
|
||||
#
|
||||
# For more on IP packets, see http://www.networksorcery.com/enp/protocol/ip.htm
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Fixnum (4 bits) :ip_v, Default: 4
|
||||
# Fixnum (4 bits) :ip_hl, Default: 5
|
||||
# Int8 :ip_tos, Default: 0 # TODO: Break out the bits
|
||||
# Int16 :ip_len, Default: calculated
|
||||
# Int16 :ip_id, Default: calculated # IRL, hardly random.
|
||||
# Int16 :ip_frag, Default: 0 # TODO: Break out the bits
|
||||
# Int8 :ip_ttl, Default: 0xff # Changes per flavor
|
||||
# Int8 :ip_proto, Default: 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
|
||||
# Int16 :ip_sum, Default: calculated
|
||||
# Octets :ip_src
|
||||
# Octets :ip_dst
|
||||
# String :body
|
||||
#
|
||||
# Note that IPPackets will always be somewhat incorrect upon initalization,
|
||||
# and want an IPHeader#recalc() to become correct before a
|
||||
# Packet#to_f or Packet#to_w.
|
||||
class IPHeader < Struct.new(:ip_v, :ip_hl, :ip_tos, :ip_len,
|
||||
:ip_id, :ip_frag, :ip_ttl, :ip_proto,
|
||||
:ip_sum, :ip_src, :ip_dst, :body)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
@random_id = rand(0xffff)
|
||||
super(
|
||||
(args[:ip_v] || 4),
|
||||
(args[:ip_hl] || 5),
|
||||
Int8.new(args[:ip_tos]),
|
||||
Int16.new(args[:ip_len] || 20),
|
||||
Int16.new(args[:ip_id] || ip_calc_id),
|
||||
Int16.new(args[:ip_frag]),
|
||||
Int8.new(args[:ip_ttl] || 32),
|
||||
Int8.new(args[:ip_proto]),
|
||||
Int16.new(args[:ip_sum] || ip_calc_sum),
|
||||
Octets.new.read(args[:ip_src] || "\x00\x00\x00\x00"),
|
||||
Octets.new.read(args[:ip_dst] || "\x00\x00\x00\x00"),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
byte_v_hl = [(self.ip_v << 4) + self.ip_hl].pack("C")
|
||||
byte_v_hl + (self.to_a[2,10].map {|x| x.to_s}.join)
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:ip_v] = str[0,1].unpack("C").first >> 4
|
||||
self[:ip_hl] = str[0,1].unpack("C").first.to_i & 0x0f
|
||||
self[:ip_tos].read(str[1,1])
|
||||
self[:ip_len].read(str[2,2])
|
||||
self[:ip_id].read(str[4,2])
|
||||
self[:ip_frag].read(str[6,2])
|
||||
self[:ip_ttl].read(str[8,1])
|
||||
self[:ip_proto].read(str[9,1])
|
||||
self[:ip_sum].read(str[10,2])
|
||||
self[:ip_src].read(str[12,4])
|
||||
self[:ip_dst].read(str[16,4])
|
||||
self[:body].read(str[20,str.size]) if str.size > 20
|
||||
self
|
||||
end
|
||||
|
||||
# Setter for the version.
|
||||
def ip_v=(i); self[:ip_v] = i.to_i; end
|
||||
# Getter for the version.
|
||||
def ip_v; self[:ip_v].to_i; end
|
||||
# Setter for the header length (divide by 4)
|
||||
def ip_hl=(i); self[:ip_hl] = i.to_i; end
|
||||
# Getter for the header length (multiply by 4)
|
||||
def ip_hl; self[:ip_hl].to_i; end
|
||||
# Setter for the differentiated services
|
||||
def ip_tos=(i); typecast i; end
|
||||
# Getter for the differentiated services
|
||||
def ip_tos; self[:ip_tos].to_i; end
|
||||
# Setter for total length.
|
||||
def ip_len=(i); typecast i; end
|
||||
# Getter for total length.
|
||||
def ip_len; self[:ip_len].to_i; end
|
||||
# Setter for the identication number.
|
||||
def ip_id=(i); typecast i; end
|
||||
# Getter for the identication number.
|
||||
def ip_id; self[:ip_id].to_i; end
|
||||
# Setter for the fragmentation ID.
|
||||
def ip_frag=(i); typecast i; end
|
||||
# Getter for the fragmentation ID.
|
||||
def ip_frag; self[:ip_frag].to_i; end
|
||||
# Setter for the time to live.
|
||||
def ip_ttl=(i); typecast i; end
|
||||
# Getter for the time to live.
|
||||
def ip_ttl; self[:ip_ttl].to_i; end
|
||||
# Setter for the protocol number.
|
||||
def ip_proto=(i); typecast i; end
|
||||
# Getter for the protocol number.
|
||||
def ip_proto; self[:ip_proto].to_i; end
|
||||
# Setter for the checksum.
|
||||
def ip_sum=(i); typecast i; end
|
||||
# Getter for the checksum.
|
||||
def ip_sum; self[:ip_sum].to_i; end
|
||||
# Setter for the source IP address.
|
||||
def ip_src=(i)
|
||||
case i
|
||||
when Numeric
|
||||
self[:ip_src] = Octets.new.read([i].pack("N"))
|
||||
when Octets
|
||||
self[:ip_src] = i
|
||||
else
|
||||
typecast i
|
||||
end
|
||||
end
|
||||
# Getter for the source IP address.
|
||||
def ip_src; self[:ip_src].to_i; end
|
||||
# Setter for the destination IP address.
|
||||
def ip_dst=(i)
|
||||
case i
|
||||
when Numeric
|
||||
self[:ip_dst] = Octets.new.read([i].pack("N"))
|
||||
when Octets
|
||||
self[:ip_dst] = i
|
||||
else
|
||||
typecast i
|
||||
end
|
||||
end
|
||||
# Getter for the destination IP address.
|
||||
def ip_dst; self[:ip_dst].to_i; end
|
||||
|
||||
# Calulcate the true length of the packet.
|
||||
def ip_calc_len
|
||||
(ip_hl * 4) + body.to_s.length
|
||||
end
|
||||
|
||||
# Return the claimed header length
|
||||
def ip_hlen
|
||||
(ip_hl * 4)
|
||||
end
|
||||
|
||||
# Calculate the true checksum of the packet.
|
||||
# (Yes, this is the long way to do it, but it's e-z-2-read for mathtards like me.)
|
||||
def ip_calc_sum
|
||||
checksum = (((self.ip_v << 4) + self.ip_hl) << 8) + self.ip_tos
|
||||
checksum += self.ip_len
|
||||
checksum += self.ip_id
|
||||
checksum += self.ip_frag
|
||||
checksum += (self.ip_ttl << 8) + self.ip_proto
|
||||
checksum += (self.ip_src >> 16)
|
||||
checksum += (self.ip_src & 0xffff)
|
||||
checksum += (self.ip_dst >> 16)
|
||||
checksum += (self.ip_dst & 0xffff)
|
||||
checksum = checksum % 0xffff
|
||||
checksum = 0xffff - checksum
|
||||
checksum == 0 ? 0xffff : checksum
|
||||
end
|
||||
|
||||
# Retrieve the IP ID
|
||||
def ip_calc_id
|
||||
@random_id
|
||||
end
|
||||
|
||||
# Sets a more readable IP address. If you wants to manipulate individual octets,
|
||||
# (eg, for host scanning in one network), it would be better use ip_src.o1 through
|
||||
# ip_src.o4 instead.
|
||||
def ip_saddr=(addr)
|
||||
self[:ip_src].read_quad(addr)
|
||||
end
|
||||
|
||||
# Returns a more readable IP source address.
|
||||
def ip_saddr
|
||||
self[:ip_src].to_x
|
||||
end
|
||||
|
||||
# Sets a more readable IP address.
|
||||
def ip_daddr=(addr)
|
||||
self[:ip_dst].read_quad(addr)
|
||||
end
|
||||
|
||||
# Returns a more readable IP destination address.
|
||||
def ip_daddr
|
||||
self[:ip_dst].to_x
|
||||
end
|
||||
|
||||
# Translate various formats of IPv4 Addresses to an array of digits.
|
||||
def self.octet_array(addr)
|
||||
if addr.class == String
|
||||
oa = addr.split('.').collect {|x| x.to_i}
|
||||
elsif addr.class == Fixnum
|
||||
oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
|
||||
elsif addr.class == Bignum
|
||||
oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
|
||||
elsif addr.class == Array
|
||||
oa = addr
|
||||
else
|
||||
raise ArgumentError, "IP Address should be a dotted quad string, an array of ints, or a bignum"
|
||||
end
|
||||
end
|
||||
|
||||
# Recalculate the calculated IP fields. Valid arguments are:
|
||||
# :all
|
||||
# :ip_len
|
||||
# :ip_sum
|
||||
# :ip_id
|
||||
def ip_recalc(arg=:all)
|
||||
case arg
|
||||
when :ip_len
|
||||
self.ip_len=ip_calc_len
|
||||
when :ip_sum
|
||||
self.ip_sum=ip_calc_sum
|
||||
when :ip_id
|
||||
@random_id = rand(0xffff)
|
||||
when :all
|
||||
self.ip_id= ip_calc_id
|
||||
self.ip_len= ip_calc_len
|
||||
self.ip_sum= ip_calc_sum
|
||||
else
|
||||
raise ArgumentError, "No such field `#{arg}'"
|
||||
end
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
alias :ip_src_readable :ip_saddr
|
||||
alias :ip_dst_readable :ip_daddr
|
||||
|
||||
def ip_id_readable
|
||||
"0x%04x" % ip_id
|
||||
end
|
||||
|
||||
def ip_sum_readable
|
||||
"0x%04x" % ip_sum
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# IPPacket is used to construct IP packets. They contain an EthHeader, an IPHeader, and usually
|
||||
# a transport-layer protocol such as UDPHeader, TCPHeader, or ICMPHeader.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# require 'packetfu'
|
||||
# ip_pkt = PacketFu::IPPacket.new
|
||||
# ip_pkt.ip_saddr="10.20.30.40"
|
||||
# ip_pkt.ip_daddr="192.168.1.1"
|
||||
# ip_pkt.ip_proto=1
|
||||
# ip_pkt.ip_ttl=64
|
||||
# ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+
|
||||
# "Lovingly hand-crafted echo responses delivered directly to your door."
|
||||
# ip_pkt.recalc
|
||||
# ip_pkt.to_f('/tmp/ip.pcap')
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# :eth
|
||||
# A pre-generated EthHeader object.
|
||||
# :ip
|
||||
# A pre-generated IPHeader object.
|
||||
# :flavor
|
||||
# TODO: Sets the "flavor" of the IP packet. This might include known sets of IP options, and
|
||||
# certainly known starting TTLs.
|
||||
# :config
|
||||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class IPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 34
|
||||
return false unless EthPacket.can_parse? str
|
||||
if str[12,2] == "\x08\x00"
|
||||
if 1.respond_to? :ord
|
||||
ipv = str[14,1][0].ord >> 4
|
||||
else
|
||||
ipv = str[14,1][0] >> 4
|
||||
end
|
||||
return true if ipv == 4
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
# Creates a new IPPacket object.
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
||||
@eth_header.body=@ip_header
|
||||
|
||||
@headers = [@eth_header, @ip_header]
|
||||
super
|
||||
end
|
||||
|
||||
# Peek provides summary data on packet contents.
|
||||
def peek_format
|
||||
peek_data = ["I "]
|
||||
peek_data << "%-5d" % to_s.size
|
||||
peek_data << "%-21s" % "#{ip_saddr}"
|
||||
peek_data << "->"
|
||||
peek_data << "%21s" % "#{ip_daddr}"
|
||||
peek_data << "%23s" % "I:"
|
||||
peek_data << "%04x" % ip_id.to_i
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,251 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# AddrIpv6 handles addressing for IPv6Header
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Int32 :a1
|
||||
# Int32 :a2
|
||||
# Int32 :a3
|
||||
# Int32 :a4
|
||||
class AddrIpv6 < Struct.new(:a1, :a2, :a3, :a4)
|
||||
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
Int32.new(args[:a1]),
|
||||
Int32.new(args[:a2]),
|
||||
Int32.new(args[:a3]),
|
||||
Int32.new(args[:a4]))
|
||||
end
|
||||
|
||||
# Returns the address in string format.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Returns the address as a fairly ginormous integer.
|
||||
def to_i
|
||||
(a1.to_i << 96) + (a2.to_i << 64) + (a3.to_i << 32) + a4.to_i
|
||||
end
|
||||
|
||||
# Returns the address as a colon-delimited hex string.
|
||||
def to_x
|
||||
IPAddr.new(self.to_i, Socket::AF_INET6).to_s
|
||||
end
|
||||
|
||||
# Reads in a string and casts it as an IPv6 address
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:a1].read str[0,4]
|
||||
self[:a2].read str[4,4]
|
||||
self[:a3].read str[8,4]
|
||||
self[:a4].read str[12,4]
|
||||
self
|
||||
end
|
||||
|
||||
# Reads in a colon-delimited hex string and casts it as an IPv6 address.
|
||||
def read_x(str)
|
||||
addr = IPAddr.new(str).to_i
|
||||
self[:a1]=Int32.new(addr >> 96)
|
||||
self[:a2]=Int32.new((addr & 0x00000000ffffffff0000000000000000) >> 64)
|
||||
self[:a3]=Int32.new((addr & 0x0000000000000000ffffffff00000000) >> 32)
|
||||
self[:a4]=Int32.new(addr & 0x000000000000000000000000ffffffff)
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# IPv6Header is complete IPv6 struct, used in IPv6Packet.
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Fixnum (4 bits) :ipv6_v Default: 6 # Versiom
|
||||
# Fixnum (8 bits) :ipv6_class Defualt: 0 # Class
|
||||
# Fixnum (20 bits) :ipv6_label Defualt: 0 # Label
|
||||
# Int16 :ipv6_len Default: calc # Payload length
|
||||
# Int8 :ipv6_next # Next Header
|
||||
# Int8 :ipv6_hop Default: 0xff # Hop limit
|
||||
# AddrIpv6 :ipv6_src
|
||||
# AddrIpv6 :ipv6_dst
|
||||
# String :body
|
||||
class IPv6Header < Struct.new(:ipv6_v, :ipv6_class, :ipv6_label,
|
||||
:ipv6_len, :ipv6_next, :ipv6_hop,
|
||||
:ipv6_src, :ipv6_dst, :body)
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
(args[:ipv6_v] || 6),
|
||||
(args[:ipv6_class] || 0),
|
||||
(args[:ipv6_label] || 0),
|
||||
Int16.new(args[:ipv6_len]),
|
||||
Int8.new(args[:ipv6_next]),
|
||||
Int8.new(args[:ipv6_hop] || 0xff),
|
||||
AddrIpv6.new.read(args[:ipv6_src] || ("\x00" * 16)),
|
||||
AddrIpv6.new.read(args[:ipv6_dst] || ("\x00" * 16)),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
bytes_v_class_label = [(self.ipv6_v << 28) +
|
||||
(self.ipv6_class << 20) +
|
||||
self.ipv6_label].pack("N")
|
||||
bytes_v_class_label + (self.to_a[3,6].map {|x| x.to_s}.join)
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:ipv6_v] = str[0,1].unpack("C").first >> 4
|
||||
self[:ipv6_class] = (str[0,2].unpack("n").first & 0x0ff0) >> 4
|
||||
self[:ipv6_label] = str[0,4].unpack("N").first & 0x000fffff
|
||||
self[:ipv6_len].read(str[4,2])
|
||||
self[:ipv6_next].read(str[6,1])
|
||||
self[:ipv6_hop].read(str[7,1])
|
||||
self[:ipv6_src].read(str[8,16])
|
||||
self[:ipv6_dst].read(str[24,16])
|
||||
self[:body].read(str[40,str.size]) if str.size > 40
|
||||
self
|
||||
end
|
||||
|
||||
# Setter for the version (usually, 6).
|
||||
def ipv6_v=(i); self[:ip_v] = i.to_i; end
|
||||
# Getter for the version (usually, 6).
|
||||
def ipv6_v; self[:ipv6_v].to_i; end
|
||||
# Setter for the traffic class.
|
||||
def ipv6_class=(i); self[:ip_class] = i.to_i; end
|
||||
# Getter for the traffic class.
|
||||
def ipv6_class; self[:ipv6_class].to_i; end
|
||||
# Setter for the flow label.
|
||||
def ipv6_label=(i); self[:ip_label] = i.to_i; end
|
||||
# Getter for the flow label.
|
||||
def ipv6_label; self[:ipv6_label].to_i; end
|
||||
# Setter for the payload length.
|
||||
def ipv6_len=(i); typecast i; end
|
||||
# Getter for the payload length.
|
||||
def ipv6_len; self[:ipv6_len].to_i; end
|
||||
# Setter for the next protocol header.
|
||||
def ipv6_next=(i); typecast i; end
|
||||
# Getter for the next protocol header.
|
||||
def ipv6_next; self[:ipv6_next].to_i; end
|
||||
# Setter for the hop limit.
|
||||
def ipv6_hop=(i); typecast i; end
|
||||
# Getter for the hop limit.
|
||||
def ipv6_hop; self[:ipv6_hop].to_i; end
|
||||
# Setter for the source address.
|
||||
def ipv6_src=(i); typecast i; end
|
||||
# Getter for the source address.
|
||||
def ipv6_src; self[:ipv6_src].to_i; end
|
||||
# Setter for the destination address.
|
||||
def ipv6_dst=(i); typecast i; end
|
||||
# Getter for the destination address.
|
||||
def ipv6_dst; self[:ipv6_dst].to_i; end
|
||||
|
||||
# Calculates the payload length.
|
||||
def ipv6_calc_len
|
||||
self[:ipv6_len] = body.to_s.length
|
||||
end
|
||||
|
||||
# Recalculates the calculatable fields for this object.
|
||||
def ipv6_recalc(arg=:all)
|
||||
case arg
|
||||
when :ipv6_len
|
||||
ipv6_calc_len
|
||||
when :all
|
||||
ipv6_recalc(:len)
|
||||
end
|
||||
end
|
||||
|
||||
# Get the source address in a more readable form.
|
||||
def ipv6_saddr
|
||||
self[:ipv6_src].to_x
|
||||
end
|
||||
|
||||
# Set the source address in a more readable form.
|
||||
def ipv6_saddr=(str)
|
||||
self[:ipv6_src].read_x(str)
|
||||
end
|
||||
|
||||
# Get the destination address in a more readable form.
|
||||
def ipv6_daddr
|
||||
self[:ipv6_dst].to_x
|
||||
end
|
||||
|
||||
# Set the destination address in a more readable form.
|
||||
def ipv6_daddr=(str)
|
||||
self[:ipv6_dst].read_x(str)
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
alias :ipv6_src_readable :ipv6_saddr
|
||||
alias :ipv6_dst_readable :ipv6_daddr
|
||||
|
||||
end # class IPv6Header
|
||||
|
||||
# IPv6Packet is used to construct IPv6 Packets. They contain an EthHeader and an IPv6Header, and in
|
||||
# the distant, unknowable future, will take interesting IPv6ish payloads.
|
||||
#
|
||||
# This mostly complete, but not very useful. It's intended primarily as an example protocol.
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# :eth
|
||||
# A pre-generated EthHeader object.
|
||||
# :ip
|
||||
# A pre-generated IPHeader object.
|
||||
# :flavor
|
||||
# TODO: Sets the "flavor" of the IPv6 packet. No idea what this will look like, haven't done much IPv6 fingerprinting.
|
||||
# :config
|
||||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class IPv6Packet < Packet
|
||||
|
||||
attr_accessor :eth_header, :ipv6_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless str.size >= 54
|
||||
return false unless str[12,2] == "\x86\xdd"
|
||||
true
|
||||
end
|
||||
|
||||
def read(str=nil,args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ipv6_header.read(str[14,str.size])
|
||||
@eth_header.body = @ipv6_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = (args[:eth] || EthHeader.new)
|
||||
@ipv6_header = (args[:ipv6] || IPv6Header.new)
|
||||
@eth_header.eth_proto = 0x86dd
|
||||
@eth_header.body=@ipv6_header
|
||||
@headers = [@eth_header, @ipv6_header]
|
||||
super
|
||||
end
|
||||
|
||||
# Peek provides summary data on packet contents.
|
||||
def peek(args={})
|
||||
peek_data = ["6 "]
|
||||
peek_data << "%-5d" % self.to_s.size
|
||||
peek_data << "%-31s" % self.ipv6_saddr
|
||||
peek_data << "-> "
|
||||
peek_data << "%-31s" % self.ipv6_daddr
|
||||
peek_data << " N:"
|
||||
peek_data << self.ipv6_next.to_s(16)
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,241 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols
|
||||
# rely on UDP, such as DNS and World of Warcraft.
|
||||
#
|
||||
# For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm
|
||||
#
|
||||
# ==== Header Definition
|
||||
# Int16 :udp_src
|
||||
# Int16 :udp_dst
|
||||
# Int16 :udp_len Default: calculated
|
||||
# Int16 :udp_sum Default: 0. Often calculated.
|
||||
# String :body
|
||||
class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
|
||||
|
||||
include StructFu
|
||||
|
||||
def initialize(args={})
|
||||
super(
|
||||
Int16.new(args[:udp_src]),
|
||||
Int16.new(args[:udp_dst]),
|
||||
Int16.new(args[:udp_len] || udp_calc_len),
|
||||
Int16.new(args[:udp_sum]),
|
||||
StructFu::String.new.read(args[:body])
|
||||
)
|
||||
end
|
||||
|
||||
# Returns the object in string form.
|
||||
def to_s
|
||||
self.to_a.map {|x| x.to_s}.join
|
||||
end
|
||||
|
||||
# Reads a string to populate the object.
|
||||
def read(str)
|
||||
force_binary(str)
|
||||
return self if str.nil?
|
||||
self[:udp_src].read(str[0,2])
|
||||
self[:udp_dst].read(str[2,2])
|
||||
self[:udp_len].read(str[4,2])
|
||||
self[:udp_sum].read(str[6,2])
|
||||
self[:body].read(str[8,str.size])
|
||||
self
|
||||
end
|
||||
|
||||
# Setter for the UDP source port.
|
||||
def udp_src=(i); typecast i; end
|
||||
# Getter for the UDP source port.
|
||||
def udp_src; self[:udp_src].to_i; end
|
||||
# Setter for the UDP destination port.
|
||||
def udp_dst=(i); typecast i; end
|
||||
# Getter for the UDP destination port.
|
||||
def udp_dst; self[:udp_dst].to_i; end
|
||||
# Setter for the length field. Usually should be recalc()'ed instead.
|
||||
def udp_len=(i); typecast i; end
|
||||
# Getter for the length field.
|
||||
def udp_len; self[:udp_len].to_i; end
|
||||
# Setter for the checksum. Usually should be recalc()'ed instad.
|
||||
def udp_sum=(i); typecast i; end
|
||||
# Getter for the checksum.
|
||||
def udp_sum; self[:udp_sum].to_i; end
|
||||
|
||||
# Returns the true length of the UDP packet.
|
||||
def udp_calc_len
|
||||
body.to_s.size + 8
|
||||
end
|
||||
|
||||
# Recalculates calculated fields for UDP.
|
||||
def udp_recalc(args=:all)
|
||||
arg = arg.intern if arg.respond_to? :intern
|
||||
case args
|
||||
when :udp_len
|
||||
self.udp_len = udp_calc_len
|
||||
when :all
|
||||
self.udp_recalc(:udp_len)
|
||||
else
|
||||
raise ArgumentError, "No such field `#{arg}'"
|
||||
end
|
||||
end
|
||||
|
||||
# Equivalent to udp_src.to_i
|
||||
def udp_sport
|
||||
self.udp_src
|
||||
end
|
||||
|
||||
# Equivalent to udp_src=
|
||||
def udp_sport=(arg)
|
||||
self.udp_src=(arg)
|
||||
end
|
||||
|
||||
# Equivalent to udp_dst
|
||||
def udp_dport
|
||||
self.udp_dst
|
||||
end
|
||||
|
||||
# Equivalent to udp_dst=
|
||||
def udp_dport=(arg)
|
||||
self.udp_dst=(arg)
|
||||
end
|
||||
|
||||
# Readability aliases
|
||||
|
||||
def udp_sum_readable
|
||||
"0x%04x" % udp_sum
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# UDPPacket is used to construct UDP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# udp_pkt = PacketFu::UDPPacket.new
|
||||
# udp_pkt.udp_src=rand(0xffff-1024) + 1024
|
||||
# udp_pkt.udp_dst=53
|
||||
#
|
||||
# udp_pkt.ip_saddr="1.2.3.4"
|
||||
# udp_pkt.ip_daddr="10.20.30.40"
|
||||
#
|
||||
# udp_pkt.recalc
|
||||
# udp_pkt.to_f('/tmp/udp.pcap')
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# :eth
|
||||
# A pre-generated EthHeader object.
|
||||
# :ip
|
||||
# A pre-generated IPHeader object.
|
||||
# :flavor
|
||||
# TODO: Sets the "flavor" of the UDP packet. UDP packets don't tend have a lot of
|
||||
# flavor, but their underlying ip headers do.
|
||||
# :config
|
||||
# A hash of return address details, often the output of Utils.whoami?
|
||||
class UDPPacket < Packet
|
||||
|
||||
attr_accessor :eth_header, :ip_header, :udp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 28
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless str[23,1] == "\x11"
|
||||
return true
|
||||
end
|
||||
|
||||
def read(str=nil, args={})
|
||||
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
||||
@eth_header.read(str)
|
||||
@ip_header.read(str[14,str.size])
|
||||
@eth_header.body = @ip_header
|
||||
if args[:strip]
|
||||
udp_len = str[16,2].unpack("n")[0] - 20
|
||||
@udp_header.read(str[14+(@ip_header.ip_hlen),udp_len])
|
||||
else
|
||||
@udp_header.read(str[14+(@ip_header.ip_hlen),str.size])
|
||||
end
|
||||
@ip_header.body = @udp_header
|
||||
super(args)
|
||||
self
|
||||
end
|
||||
|
||||
def initialize(args={})
|
||||
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||
@ip_header = IPHeader.new(args).read(args[:ip])
|
||||
@ip_header.ip_proto=0x11
|
||||
@udp_header = UDPHeader.new(args).read(args[:icmp])
|
||||
@ip_header.body = @udp_header
|
||||
@eth_header.body = @ip_header
|
||||
@headers = [@eth_header, @ip_header, @udp_header]
|
||||
super
|
||||
udp_calc_sum
|
||||
end
|
||||
|
||||
# udp_calc_sum() computes the UDP checksum, and is called upon intialization.
|
||||
# It usually should be called just prior to dropping packets to a file or on the wire.
|
||||
def udp_calc_sum
|
||||
# This is /not/ delegated down to @udp_header since we need info
|
||||
# from the IP header, too.
|
||||
checksum = (ip_src.to_i >> 16)
|
||||
checksum += (ip_src.to_i & 0xffff)
|
||||
checksum += (ip_dst.to_i >> 16)
|
||||
checksum += (ip_dst.to_i & 0xffff)
|
||||
checksum += 0x11
|
||||
checksum += udp_len.to_i
|
||||
checksum += udp_src.to_i
|
||||
checksum += udp_dst.to_i
|
||||
checksum += udp_len.to_i
|
||||
if udp_len.to_i >= 8
|
||||
# For IP trailers. This isn't very reliable. :/
|
||||
real_udp_payload = payload.to_s[0,(udp_len.to_i-8)]
|
||||
else
|
||||
# I'm not going to mess with this right now.
|
||||
real_udp_payload = payload
|
||||
end
|
||||
chk_payload = (real_udp_payload.size % 2 == 0 ? real_udp_payload : real_udp_payload + "\x00")
|
||||
chk_payload.unpack("n*").each {|x| checksum = checksum+x}
|
||||
checksum = checksum % 0xffff
|
||||
checksum = 0xffff - checksum
|
||||
checksum == 0 ? 0xffff : checksum
|
||||
@udp_header.udp_sum = checksum
|
||||
end
|
||||
|
||||
# udp_recalc() recalculates various fields of the UDP packet. Valid arguments are:
|
||||
#
|
||||
# :all
|
||||
# Recomputes all calculated fields.
|
||||
# :udp_sum
|
||||
# Recomputes the UDP checksum.
|
||||
# :udp_len
|
||||
# Recomputes the UDP length.
|
||||
def udp_recalc(args=:all)
|
||||
case args
|
||||
when :udp_len
|
||||
@udp_header.udp_recalc
|
||||
when :udp_sum
|
||||
udp_calc_sum
|
||||
when :all
|
||||
@udp_header.udp_recalc
|
||||
udp_calc_sum
|
||||
else
|
||||
raise ArgumentError, "No such field `#{arg}'"
|
||||
end
|
||||
end
|
||||
|
||||
# Peek provides summary data on packet contents.
|
||||
def peek_format
|
||||
peek_data = ["U "]
|
||||
peek_data << "%-5d" % self.to_s.size
|
||||
peek_data << "%-21s" % "#{self.ip_saddr}:#{self.udp_sport}"
|
||||
peek_data << "->"
|
||||
peek_data << "%21s" % "#{self.ip_daddr}:#{self.udp_dport}"
|
||||
peek_data << "%23s" % "I:"
|
||||
peek_data << "%04x" % self.ip_id
|
||||
peek_data.join
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,295 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
# StructFu, a nifty way to leverage Ruby's built in Struct class
|
||||
# to create meaningful binary data.
|
||||
|
||||
module StructFu
|
||||
|
||||
# Normally, self.size and self.length will refer to the Struct
|
||||
# size as an array. It's a hassle to redefine, so this introduces some
|
||||
# shorthand to get at the size of the resultant string.
|
||||
def sz
|
||||
self.to_s.size
|
||||
end
|
||||
|
||||
alias len sz
|
||||
|
||||
# Typecast is used mostly by packet header classes, such as IPHeader,
|
||||
# TCPHeader, and the like. It takes an argument, and casts it to the
|
||||
# expected type for that element.
|
||||
def typecast(i)
|
||||
c = caller[0].match(/.*`([^']+)='/)[1]
|
||||
self[c.intern].read i
|
||||
end
|
||||
|
||||
# Used like typecast(), but specifically for casting Strings to StructFu::Strings.
|
||||
def body=(i)
|
||||
if i.kind_of? ::String
|
||||
typecast(i)
|
||||
elsif i.kind_of? StructFu
|
||||
self[:body] = i
|
||||
elsif i.nil?
|
||||
self[:body] = StructFu::String.new.read("")
|
||||
else
|
||||
raise ArgumentError, "Can't cram a #{i.class} into a StructFu :body"
|
||||
end
|
||||
end
|
||||
|
||||
# Handle deep copies correctly. Marshal in 1.9, re-read myself on 1.8
|
||||
def clone
|
||||
begin
|
||||
Marshal.load(Marshal.dump(self))
|
||||
rescue
|
||||
self.class.new.read(self.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Ints all have a value, an endianness, and a default value.
|
||||
# Note that the signedness of Int values are implicit as
|
||||
# far as the subclasses are concerned; to_i and to_f will
|
||||
# return Integer/Float versions of the input value, instead
|
||||
# of attempting to unpack the pack value. (This can be a useful
|
||||
# hint to other functions).
|
||||
#
|
||||
# ==== Header Definition
|
||||
#
|
||||
# Fixnum :value
|
||||
# Symbol :endian
|
||||
# Fixnum :width
|
||||
# Fixnum :default
|
||||
class Int < Struct.new(:value, :endian, :width, :default)
|
||||
alias :v= :value=
|
||||
alias :v :value
|
||||
alias :e= :endian=
|
||||
alias :e :endian
|
||||
alias :w= :width=
|
||||
alias :w :width
|
||||
alias :d= :default=
|
||||
alias :d :default
|
||||
|
||||
# This is a parent class definition and should not be used directly.
|
||||
def to_s
|
||||
raise StandardError, "StructFu::Int#to_s accessed, must be redefined."
|
||||
end
|
||||
|
||||
# Returns the Int as an Integer.
|
||||
def to_i
|
||||
(self.v || self.d).to_i
|
||||
end
|
||||
|
||||
# Returns the Int as a Float.
|
||||
def to_f
|
||||
(self.v || self.d).to_f
|
||||
end
|
||||
|
||||
def initialize(value=nil, endian=nil, width=nil, default=nil)
|
||||
super(value,endian,width,default=0)
|
||||
end
|
||||
|
||||
# Reads either an Integer or a packed string, and populates the value accordingly.
|
||||
def read(i)
|
||||
self.v = i.kind_of?(Integer) ? i.to_i : i.to_s.unpack(@packstr).first
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Int8 is a one byte value.
|
||||
class Int8 < Int
|
||||
|
||||
def initialize(v=nil)
|
||||
super(v,nil,w=1)
|
||||
@packstr = "C"
|
||||
end
|
||||
|
||||
# Returns a one byte value as a packed string.
|
||||
def to_s
|
||||
[(self.v || self.d)].pack("C")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Int16 is a two byte value.
|
||||
class Int16 < Int
|
||||
def initialize(v=nil, e=:big)
|
||||
super(v,e,w=2)
|
||||
@packstr = (self.e == :big) ? "n" : "v"
|
||||
end
|
||||
|
||||
# Returns a two byte value as a packed string.
|
||||
def to_s
|
||||
@packstr = (self.e == :big) ? "n" : "v"
|
||||
[(self.v || self.d)].pack(@packstr)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Int16be is a two byte value in big-endian format. The endianness cannot be altered.
|
||||
class Int16be < Int16
|
||||
undef :endian=
|
||||
end
|
||||
|
||||
# Int16le is a two byte value in little-endian format. The endianness cannot be altered.
|
||||
class Int16le < Int16
|
||||
undef :endian=
|
||||
def initialize(v=nil, e=:little)
|
||||
super(v,e)
|
||||
@packstr = (self.e == :big) ? "n" : "v"
|
||||
end
|
||||
end
|
||||
|
||||
# Int32 is a four byte value.
|
||||
class Int32 < Int
|
||||
def initialize(v=nil, e=:big)
|
||||
super(v,e,w=4)
|
||||
@packstr = (self.e == :big) ? "N" : "V"
|
||||
end
|
||||
|
||||
# Returns a four byte value as a packed string.
|
||||
def to_s
|
||||
@packstr = (self.e == :big) ? "N" : "V"
|
||||
[(self.v || self.d)].pack(@packstr)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Int32be is a four byte value in big-endian format. The endianness cannot be altered.
|
||||
class Int32be < Int32
|
||||
undef :endian=
|
||||
end
|
||||
|
||||
# Int32le is a four byte value in little-endian format. The endianness cannot be altered.
|
||||
class Int32le < Int32
|
||||
undef :endian=
|
||||
def initialize(v=nil, e=:little)
|
||||
super(v,e)
|
||||
end
|
||||
end
|
||||
|
||||
# Strings are just like regular strings, except it comes with a read() function
|
||||
# so that it behaves like other StructFu elements.
|
||||
class String < ::String
|
||||
def read(str)
|
||||
str = str.to_s
|
||||
self.replace str
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
# Provides a primitive for creating strings, preceeded by
|
||||
# an Int type of length. By default, a string of length zero with
|
||||
# a one-byte length is presumed.
|
||||
#
|
||||
# Note that IntStrings aren't used for much, but it seemed like a good idea at the time.
|
||||
class IntString < Struct.new(:int, :string, :mode)
|
||||
|
||||
def initialize(string='',int=Int8,mode=nil)
|
||||
if int < Int
|
||||
super(int.new,string,mode)
|
||||
calc
|
||||
else
|
||||
raise "IntStrings need a StructFu::Int for a length."
|
||||
end
|
||||
end
|
||||
|
||||
# Calculates the size of a string, and sets it as the value.
|
||||
def calc
|
||||
int.v = string.to_s.size
|
||||
self.to_s
|
||||
end
|
||||
|
||||
# Returns the object as a string, depending on the mode set upon object creation.
|
||||
def to_s
|
||||
if mode == :parse
|
||||
"#{int}" + [string].pack("a#{len}")
|
||||
elsif mode == :fix
|
||||
self.int.v = string.size
|
||||
"#{int}#{string}"
|
||||
else
|
||||
"#{int}#{string}"
|
||||
end
|
||||
end
|
||||
|
||||
# By redefining #string=, we can ensure the correct value
|
||||
# is calculated upon assignment. If you'd prefer to have
|
||||
# an incorrect value, use the syntax, obj[:string]="value"
|
||||
# instead. Note, by using the alternate form, you must
|
||||
# #calc before you can trust the int's value. Think of the =
|
||||
# assignment as "set to equal," while the []= assignment
|
||||
# as "boxing in" the value. Maybe.
|
||||
def string=(s)
|
||||
self[:string] = s
|
||||
calc
|
||||
end
|
||||
|
||||
# Shorthand for querying a length. Note that the usual "length"
|
||||
# and "size" refer to the number of elements of this struct.
|
||||
def len
|
||||
self[:int].value
|
||||
end
|
||||
|
||||
# Override the size, if you must.
|
||||
def len=(i)
|
||||
self[:int].value=i
|
||||
end
|
||||
|
||||
# Read takes a string, assumes an int width as previously
|
||||
# defined upon initialization, but makes no guarantees
|
||||
# the int value isn't lying. You're on your own to test
|
||||
# for that (or use parse() with a :mode set).
|
||||
def read(s)
|
||||
unless s[0,int.width].size == int.width
|
||||
raise StandardError, "String is too short for type #{int.class}"
|
||||
else
|
||||
int.read(s[0,int.width])
|
||||
self[:string] = s[int.width,s.size]
|
||||
end
|
||||
self.to_s
|
||||
end
|
||||
|
||||
# parse() is like read(), except that it interprets the string, either
|
||||
# based on the declared length, or the actual length. Which strategy
|
||||
# is used is dependant on which :mode is set (with self.mode).
|
||||
#
|
||||
# :parse : Read the length, and then read in that many bytes of the string.
|
||||
# The string may be truncated or padded out with nulls, as dictated by the value.
|
||||
#
|
||||
# :fix : Skip the length, read the rest of the string, then set the length
|
||||
# to what it ought to be.
|
||||
#
|
||||
# else : If neither of these modes are set, just perfom a normal read().
|
||||
# This is the default.
|
||||
def parse(s)
|
||||
unless s[0,int.width].size == int.width
|
||||
raise StandardError, "String is too short for type #{int.class}"
|
||||
else
|
||||
case mode
|
||||
when :parse
|
||||
int.read(s[0,int.width])
|
||||
self[:string] = s[int.width,int.value]
|
||||
if string.size < int.value
|
||||
self[:string] += ("\x00" * (int.value - self[:string].size))
|
||||
end
|
||||
when :fix
|
||||
self.string = s[int.width,s.size]
|
||||
else
|
||||
return read(s)
|
||||
end
|
||||
end
|
||||
self.to_s
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Struct
|
||||
|
||||
# Monkeypatch for Struct to include some string safety -- anything that uses
|
||||
# Struct is going to presume binary strings anyway.
|
||||
def force_binary(str)
|
||||
PacketFu.force_binary(str)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,219 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'singleton'
|
||||
module PacketFu
|
||||
|
||||
# Utils is a collection of various and sundry network utilities that are useful for packet
|
||||
# manipulation.
|
||||
class Utils
|
||||
|
||||
# Returns the MAC address of an IP address, or nil if it's not responsive to arp. Takes
|
||||
# a dotted-octect notation of the target IP address, as well as a number of parameters:
|
||||
#
|
||||
# === Parameters
|
||||
# :eth_saddr
|
||||
# Source MAC address. Defaults to "00:00:00:00:00:00".
|
||||
# :ip_saddr
|
||||
# Source IP address. Defaults to "0.0.0.0"
|
||||
# :flavor
|
||||
# The flavor of the ARP request. Defaults to :none.
|
||||
# :timeout
|
||||
# Timeout in seconds. Defaults to 3.
|
||||
#
|
||||
# === Example
|
||||
# PacketFu::Utils::arp("192.168.1.1") #=> "00:18:39:01:33:70"
|
||||
# PacketFu::Utils::arp("192.168.1.1", :timeout => 5, :flavor => :hp_deskjet)
|
||||
#
|
||||
# === Warning
|
||||
#
|
||||
# It goes without saying, spewing forged ARP packets on your network is a great way to really
|
||||
# irritate your co-workers.
|
||||
def self.arp(target_ip,args={})
|
||||
iface = args[:iface] || :eth0
|
||||
args[:config] ||= whoami?(:iface => iface)
|
||||
arp_pkt = PacketFu::ARPPacket.new(:flavor => (args[:flavor] || :none), :config => args[:config])
|
||||
arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
|
||||
arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
|
||||
arp_pkt.arp_daddr_ip = target_ip
|
||||
# Stick the Capture object in its own thread.
|
||||
cap_thread = Thread.new do
|
||||
target_mac = nil
|
||||
cap = PacketFu::Capture.new(:iface => iface, :start => true,
|
||||
:filter => "arp src #{target_ip} and ether dst #{arp_pkt.eth_saddr}")
|
||||
arp_pkt.to_w(iface) # Shorthand for sending single packets to the default interface.
|
||||
timeout = 0
|
||||
while target_mac.nil? && timeout <= (args[:timeout] || 3)
|
||||
if cap.save > 0
|
||||
arp_response = PacketFu::Packet.parse(cap.array[0])
|
||||
target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip = target_ip
|
||||
end
|
||||
timeout += 0.1
|
||||
sleep 0.1 # Check for a response ten times per second.
|
||||
end
|
||||
target_mac
|
||||
end # cap_thread
|
||||
cap_thread.value
|
||||
end
|
||||
|
||||
# Discovers the local IP and Ethernet address, which is useful for writing
|
||||
# packets you expect to get a response to. Note, this is a noisy
|
||||
# operation; a UDP packet is generated and dropped on to the default (or named)
|
||||
# interface, and then captured (which means you need to be root to do this).
|
||||
#
|
||||
# whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
|
||||
# :ip_src_bin, :eth_dst, and :eth_daddr (the last two are usually suitable
|
||||
# for a gateway mac address). It's most useful as an argument to
|
||||
# PacketFu::Config.new, or as an argument to the many Packet constructors.
|
||||
#
|
||||
# Note that if you have multiple interfaces with the same route (such as when
|
||||
# wlan0 and eth0 are associated to the same network), the "first" one
|
||||
# according to Pcap.lookupdev will be used, regardless of which :iface you
|
||||
# pick.
|
||||
#
|
||||
# === Parameters
|
||||
# :iface => "eth0"
|
||||
# An interface to listen for packets on. Note that since we rely on the OS to send the probe packet,
|
||||
# you will need to specify a target which will use this interface.
|
||||
# :target => "1.2.3.4"
|
||||
# A target IP address. By default, a packet will be sent to a random address in the 177/8 network.
|
||||
# Since this network is IANA reserved (for now), this network should be handled by your default gateway
|
||||
# and default interface.
|
||||
def self.whoami?(args={})
|
||||
unless args.kind_of? Hash
|
||||
raise ArgumentError, "Argument to `whoami?' must be a Hash"
|
||||
end
|
||||
if args[:iface].to_s =~ /^lo/ # Linux loopback more or less. Need a switch for windows loopback, too.
|
||||
dst_host = "127.0.0.1"
|
||||
else
|
||||
dst_host = (args[:target] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
|
||||
end
|
||||
|
||||
dst_port = rand(0xffff-1024)+1024
|
||||
msg = "PacketFu whoami? packet #{(Time.now.to_i + rand(0xffffff)+1)}"
|
||||
iface = (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || :lo ).to_s
|
||||
cap = PacketFu::Capture.new(:iface => iface, :promisc => false, :start => true, :filter => "udp and dst host #{dst_host} and dst port #{dst_port}")
|
||||
udp_sock = UDPSocket.new
|
||||
udp_sock.send(msg,0,dst_host,dst_port)
|
||||
udp_sock = nil
|
||||
cap.save
|
||||
pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
|
||||
timeout = 0
|
||||
while timeout < 1 # Sometimes packet generation can be a little pokey.
|
||||
if pkt
|
||||
timeout = 1.1 # Cancel the timeout
|
||||
if pkt.payload == msg
|
||||
my_data = {
|
||||
:iface => (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo").to_s,
|
||||
:pcapfile => args[:pcapfile] || "/tmp/out.pcap",
|
||||
:eth_saddr => pkt.eth_saddr,
|
||||
:eth_src => pkt.eth_src.to_s,
|
||||
:ip_saddr => pkt.ip_saddr,
|
||||
:ip_src => pkt.ip_src,
|
||||
:ip_src_bin => [pkt.ip_src].pack("N"),
|
||||
:eth_dst => pkt.eth_dst.to_s,
|
||||
:eth_daddr => pkt.eth_daddr
|
||||
}
|
||||
else raise SecurityError,
|
||||
"whoami() packet doesn't match sent data. Something fishy's going on."
|
||||
end
|
||||
else
|
||||
sleep 0.1; timeout += 0.1
|
||||
cap.save
|
||||
pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
|
||||
end
|
||||
raise SocketError, "Didn't receive the whomi() packet, can't automatically configure." if !pkt
|
||||
cap = nil
|
||||
end
|
||||
my_data
|
||||
end
|
||||
|
||||
# This is a brute-force approach at trying to find a suitable interface with an IP address.
|
||||
def self.lookupdev
|
||||
# XXX cycle through eth0-9 and wlan0-9, and if a cap start throws a RuntimeErorr (and we're
|
||||
# root), it's not a good interface. Boy, really ought to fix lookupdev directly with another
|
||||
# method that returns an array rather than just the first candidate.
|
||||
end
|
||||
|
||||
# Handles ifconfig for various (okay, two) platforms.
|
||||
# Will have Windows done shortly.
|
||||
#
|
||||
# Takes an argument (either string or symbol) of the interface to look up, and
|
||||
# returns a hash which contains at least the :iface element, and if configured,
|
||||
# these additional elements:
|
||||
#
|
||||
# :eth_saddr # A human readable MAC address
|
||||
# :eth_src # A packed MAC address
|
||||
# :ip_saddr # A dotted-quad string IPv4 address
|
||||
# :ip_src # A packed IPv4 address
|
||||
# :ip4_obj # An IPAddr object with bitmask
|
||||
# :ip6_saddr # A colon-delimited hex IPv6 address, with bitmask
|
||||
# :ip6_obj # An IPAddr object with bitmask
|
||||
#
|
||||
# === Example
|
||||
# PacketFu::Utils.ifconfig :wlan0 # Not associated yet
|
||||
# #=> {:eth_saddr=>"00:1d:e0:73:9d:ff", :eth_src=>"\000\035\340s\235\377", :iface=>"wlan0"}
|
||||
# PacketFu::Utils.ifconfig("eth0") # Takes 'eth0' as default
|
||||
# #=> {:eth_saddr=>"00:1c:23:35:70:3b", :eth_src=>"\000\034#5p;", :ip_saddr=>"10.10.10.9", :ip4_obj=>#<IPAddr: IPv4:10.10.10.0/255.255.254.0>, :ip_src=>"\n\n\n\t", :iface=>"eth0", :ip6_saddr=>"fe80::21c:23ff:fe35:703b/64", :ip6_obj=>#<IPAddr: IPv6:fe80:0000:0000:0000:0000:0000:0000:0000/ffff:ffff:ffff:ffff:0000:0000:0000:0000>}
|
||||
# PacketFu::Utils.ifconfig :lo
|
||||
# #=> {:ip_saddr=>"127.0.0.1", :ip4_obj=>#<IPAddr: IPv4:127.0.0.0/255.0.0.0>, :ip_src=>"\177\000\000\001", :iface=>"lo", :ip6_saddr=>"::1/128", :ip6_obj=>#<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>}
|
||||
def self.ifconfig(iface='eth0')
|
||||
ret = {}
|
||||
iface = iface.to_s.scan(/[0-9A-Za-z]/).join # Sanitizing input, no spaces, semicolons, etc.
|
||||
case RUBY_PLATFORM
|
||||
when /linux/i
|
||||
ifconfig_data = %x[ifconfig #{iface}]
|
||||
if ifconfig_data =~ /#{iface}/i
|
||||
ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
|
||||
else
|
||||
raise ArgumentError, "Cannot ifconfig #{iface}"
|
||||
end
|
||||
real_iface = ifconfig_data.first
|
||||
ret[:iface] = real_iface.split.first.downcase
|
||||
if real_iface =~ /[\s]HWaddr[\s]+([0-9a-fA-F:]{17})/i
|
||||
ret[:eth_saddr] = $1.downcase
|
||||
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
||||
end
|
||||
ifconfig_data.each do |s|
|
||||
case s
|
||||
when /inet addr:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
||||
ret[:ip_saddr] = $1
|
||||
ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
|
||||
ret[:ip4_obj] = IPAddr.new($1)
|
||||
ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
|
||||
when /inet6 addr:[\s]*([0-9a-fA-F:\x2f]+)/
|
||||
ret[:ip6_saddr] = $1
|
||||
ret[:ip6_obj] = IPAddr.new($1)
|
||||
end
|
||||
end # linux
|
||||
when /darwin/i
|
||||
ifconfig_data = %x[ifconfig #{iface}]
|
||||
if ifconfig_data =~ /#{iface}/i
|
||||
ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
|
||||
else
|
||||
raise ArgumentError, "Cannot ifconfig #{iface}"
|
||||
end
|
||||
real_iface = ifconfig_data.first
|
||||
ret[:iface] = real_iface.split(':')[0]
|
||||
ifconfig_data.each do |s|
|
||||
case s
|
||||
when /ether[\s]([0-9a-fA-F:]{17})/i
|
||||
ret[:eth_saddr] = $1
|
||||
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
||||
when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
||||
ret[:ip_saddr] = $1
|
||||
ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
|
||||
ret[:ip4_obj] = IPAddr.new($1)
|
||||
ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
|
||||
when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
|
||||
ret[:ip6_saddr] = $1
|
||||
ret[:ip6_obj] = IPAddr.new($1)
|
||||
end
|
||||
end # darwin
|
||||
end # RUBY_PLATFORM
|
||||
ret
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -1,51 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module PacketFu
|
||||
|
||||
# Check the repo's for version release histories
|
||||
VERSION = "1.1.5" # Unscrewing the 1.1.4 gem
|
||||
|
||||
# Returns PacketFu::VERSION
|
||||
def self.version
|
||||
VERSION
|
||||
end
|
||||
|
||||
# Returns a version string in a binary format for easy comparisons.
|
||||
def self.binarize_version(str)
|
||||
if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?\..+$/)
|
||||
bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i}
|
||||
bin_version = (bin_major.to_i << 16) + (bin_minor.to_i << 8) + bin_teeny.to_i
|
||||
else
|
||||
raise ArgumentError, "Compare version malformed. Should be \x22x.y.z\x22"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the version is equal to or greater than the compare version.
|
||||
# If the current version of PacketFu is "0.3.1" for example:
|
||||
#
|
||||
# PacketFu.at_least? "0" # => true
|
||||
# PacketFu.at_least? "0.2.9" # => true
|
||||
# PacketFu.at_least? "0.3" # => true
|
||||
# PacketFu.at_least? "1" # => true after 1.0's release
|
||||
# PacketFu.at_least? "1.12" # => false
|
||||
# PacketFu.at_least? "2" # => false
|
||||
def self.at_least?(str)
|
||||
this_version = binarize_version(self.version)
|
||||
ask_version = binarize_version(str)
|
||||
this_version >= ask_version
|
||||
end
|
||||
|
||||
# Returns true if the current version is older than the compare version.
|
||||
def self.older_than?(str)
|
||||
return false if str == self.version
|
||||
this_version = binarize_version(self.version)
|
||||
ask_version = binarize_version(str)
|
||||
this_version < ask_version
|
||||
end
|
||||
|
||||
# Returns true if the current version is newer than the compare version.
|
||||
def self.newer_than?(str)
|
||||
return false if str == self.version
|
||||
!self.older_than?(str)
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue