summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING339
-rw-r--r--COPYING.LIB502
-rw-r--r--CREDITS28
-rw-r--r--ChangeLog483
-rw-r--r--Make.defines55
-rw-r--r--Make.rules213
-rw-r--r--Makefile117
-rw-r--r--README39
-rw-r--r--TODO33
-rw-r--r--VERSION1
-rwxr-xr-xchvers.sh102
-rw-r--r--default.rules193
-rw-r--r--include/version.h9
-rw-r--r--libsupp/clif.c1259
-rw-r--r--libsupp/clif.h121
-rwxr-xr-xstore.sh72
-rw-r--r--traceroute.spec59
-rw-r--r--traceroute/as_lookups.c128
-rw-r--r--traceroute/csum.c34
-rw-r--r--traceroute/extension.c237
-rw-r--r--traceroute/flowlabel.h40
-rw-r--r--traceroute/mod-dccp.c289
-rw-r--r--traceroute/mod-icmp.c247
-rw-r--r--traceroute/mod-raw.c163
-rw-r--r--traceroute/mod-tcp.c606
-rw-r--r--traceroute/mod-tcpconn.c228
-rw-r--r--traceroute/mod-udp.c234
-rw-r--r--traceroute/module.c35
-rw-r--r--traceroute/poll.c88
-rw-r--r--traceroute/random.c28
-rw-r--r--traceroute/time.c27
-rw-r--r--traceroute/traceroute.8629
-rw-r--r--traceroute/traceroute.c1693
-rw-r--r--traceroute/traceroute.h109
-rw-r--r--wrappers/Makefile15
-rw-r--r--wrappers/README.wrappers18
-rwxr-xr-xwrappers/lft117
-rwxr-xr-xwrappers/tcptraceroute78
-rw-r--r--wrappers/tcptraceroute.834
-rwxr-xr-xwrappers/tracepath63
-rwxr-xr-xwrappers/traceproto127
-rwxr-xr-xwrappers/traceroute-nanog76
42 files changed, 8968 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[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
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..2675676
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,28 @@
+Thanks for the testing, proposals, bug fixing:
+
+Robert Scheck (redhat-bugzilla@linuxnetz.de)
+Peter Bieringer (pb@bieringer.de)
+Martin Bacovsky (mbacovsk@redhat.com)
+Andy Shevchenko (andriy@asplinux.ru)
+Mike Frysinger (vapier@gentoo.org)
+Chris Ward (cward@redhat.com)
+Teran McKinney (sega01@gmail.com)
+Kaj Niemi (kajtzu@a51.org)
+Milos Malik (mmalik@redhat.com)
+Andreas Mohr (andim2@users.sourceforge.net)
+Vladz (vladz@devzero.fr)
+Daniel Baumann (daniel@debian.org)
+Filip Holec (fholec@redhat.com)
+Samuel Jero (sj323707@ohio.edu)
+Jan Synacek (jsynacek@redhat.com)
+Frederic Mangano (fmang@mg0.fr)
+Jeff (geogriffin@jsgriff.com)
+Andrew Schwartz (schwartz@amacapital.net)
+Felix Janda (felix.janda@posteo.de)
+Sergey Salnikov (serg@salnikov.ru)
+Richard Sheehan (richardsheehan@users.sourceforge.net)
+Eric Dumazet (edumazet@google.com)
+Francois Rigault <francois.rigault@amadeus.com>
+...
+maybe you too? ;)
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..ffd2100
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,483 @@
+2023-12-18 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.1.5
+
+ * Fix rfc5837 parsing (Francois Rigault)
+
+
+2023-12-13 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.1.4
+
+ * Parse interface information (rfc5837) for ICMP extensions
+
+ * Add `fastopen' tcp module option (cookie negotiation only)
+
+ * Complete tcp module option `mss' to discover possible mss clamping
+ along the path being traced (idea and testing from Francois Rigault).
+ The argument is optional now.
+
+ Changed mss is printed once in a form of `M=NUM' at the first probe
+ it was detected on. (Actually, the mss clamping performed by
+ some previous hop).
+
+ Note, some routers may return too short original fragment
+ in the time exceeded message, making the check impossible.
+ Besides that the responses may come in a different order.
+ All this can lead to a later place of the report
+ (using -N 1 can help for the order).
+
+ * Complete tcp module option `info' to print returned tcp header options too
+ (all those that can be set or altered by `-O' for tcp module).
+
+
+2023-08-30 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.1.3
+
+ * Fix command line parsing in wrappers.
+
+
+2023-02-13 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.1.2
+
+ * Fix unprivileged ICMP tracerouting with Linux kernel >= 6.1
+ (Eric Dumazet, SF bug #14)
+
+
+2022-12-27 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.1.1
+
+ * Interpret ipv4-mapped ipv6 addresses (::ffff:A.B.C.D) as true ipv4.
+
+ There are no ipv4-mapped addresses in the real network which we
+ operate on, so use just ipv4 in such cases, but allow users
+ to specify it this way for convenience.
+
+ * Return back more robast poll(2) loop handling.
+
+
+2016-03-08 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.1.0
+
+ * Improve the main loop for better interactivity.
+
+ Instead of waiting silently for maximum expiration time of probes
+ in progress, use timeout of the first probe (which will be printed
+ first from now) only.
+
+ * Speedup wait mechanism.
+
+ Traditional traceroute implementation always waited the whole timeout
+ for any probe. But if we already have some replies from the same hop,
+ or even from some next hop, we can use the round trip time
+ of such a reply as a hint to determine the actual reasonable
+ amount of time to wait.
+
+ Now the `-w' option has a form of three (in general) float values
+ separated by a comma (or a slash): `-w MAX_SECS,HERE,NEAR' .
+ (last two are optional). MAX_SECS specifies the maximum time
+ (in seconds) to wait, in any case.
+
+ The optional HERE specifies a factor to multiply the round trip time
+ of an already received response from the same hop.
+ The resulting value is used as a timeout for the probe, instead of
+ (but no more than) MAX_SECS. The optional NEAR specifies a similar
+ factor for a response from some next hop.
+ The time of the first found result is used in both cases.
+
+ First, we look for the same hop (of the probe which will be printed
+ first from now). If nothing found, then look for some next hop.
+ If nothing found, use MAX_SECS. If HERE and/or NEAR have zero values,
+ the corresponding computation is skipped.
+
+ HERE and NEAR are always set to zero if only MAX_SECS is specified
+ (which provides compatibility with previous versions). Thus, if your
+ scripts use `-w SECS', then nothing changed for you, since
+ the lonely SECS implies `-w SECS,0,0' .
+
+ Defaults are 5.0 seconds for MAX_SECS, 3.0 times for HERE and
+ 10.0 times for NEAR.
+
+ Certainly, the new algorithm can lead to premature expiry
+ (especially when response times differ at times) and printing "*"
+ instead of a time. Anyway, you can always switch this algorithm off,
+ just by specifying `-w' with the desired timeout only (fe. `-w 5').
+
+ We continue to wait whole MAX_SECS when one probe per time
+ must be sent (`--sport', `-P proto'), because it seems more harmful
+ rather than helpful to try to wait less in such cases.
+
+ To provide compatibility with 2.0.x versions, use:
+
+ traceroute -w 5
+
+ (or any other desired `-w' value).
+
+ * Hint people to use the system traceroute(8) instead of
+ tcptraceroute wrapper (by providing a stderr header).
+
+ The using of this wrapper is a little bit harmful, since it has
+ less possibilities and a little different set of options.
+
+ For those who are used to use tcptraceroute in cmdline,
+ just create a link with that name to the system traceroute.
+ When invoked as "tcp*", it then behaves as `traceroute -T'.
+ (The simple manual page added for this case in the wrapper subdir).
+
+ The original tcptraceroute had some options differ ("lpNSAE"),
+ but they was rare used. Most common "dnFifmqwst" was just the same.
+ Therefore it should be painless to use the system binary directly,
+ instead of the limited wrapper (which is still provided indeed).
+
+
+2016-02-15 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.22
+
+ * Some portability fixing and improvements (Felix Janda)
+
+ * Require clear numbers for options and arguments (Sergey Salnikov)
+
+ * Drop compilation date from the version string (Debian #774365)
+
+ * New tcp module option `reuse', which utilize SO_REUSEADDR
+ to reuse local port numbers for the huge workloads (Richard Sheehan)
+
+ * Avoid poll(2) call with spurious zero timeout in some rare cases
+ by rounding the value properly using ceil(3)
+
+
+2014-11-12 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.21
+
+ * Fix `--mtu' and `-F' working on kernels >= 3.13
+
+ * Some manual page improving (Christopher Mann)
+
+
+2014-06-14 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.20
+
+ * Describe all complementary long options in the man page (Jan Synacek)
+
+ * Use correct service name for AS lookups (Frederic Mangano)
+
+ * Avoid some rare case null dereference (geogriffin@jsgriff.com)
+
+ * Improve expiration check for simultaneous probes
+
+
+2012-11-19 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.19
+
+ * DCCP protocol support (rfc4340), by Samuel Jero
+
+ Use "-D" option for it (the protocol-specific options
+ are available too).
+
+ * Update COPYING and COPYING.LIB license files to the latest
+ published ones (due to FSF address changes etc.) (Jan Synacek)
+
+ * Add mention of "-l" option to manual (Filip Holec)
+
+
+2011-08-16 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.18
+
+ * Handle new dgram icmp sockets ("echo ping sockets"),
+ appeared in kernel 3.0 .
+
+ Now unprivileged users may perform ICMP tracerouting
+ without any special rights of the executable
+ (neither setuid bits nor cap_net_raw settings).
+ It is allowed if any group of a user matches sysctl range
+ of "net/ipv4/ping_group_range".
+
+ The support for dgram icmp way (and whether it is allowed)
+ is auto-detected at runtime. First, the traditional raw socket
+ is tried (for full compatibility reasons), then new dgram
+ socket as a fallback.
+ The icmp module now has two additional options "raw" and "dgram",
+ which cause to try one particular way only.
+
+ Note, that there is no IPv6 implementation for dgram icmp sockets
+ in kernels 3.0 yet, but new traceroute is ready for it anyway.
+
+ * New tcp module option `info' ("-T -O info"),
+ which prints all tcp flags of tcp reply from the reached
+ target host.
+
+ The flags are shown comma-separated in the same place
+ where icmp extensions is printed (ie. in `<>' brackets)
+
+ This feature is utilized by tcptraceroute wrapper now,
+ and allow it to be completely functional replacement
+ of the original tcptraceroute.
+
+ * Fix determination of system-wide ECN setings for tcp module.
+
+ Since the kernel 2.6.31 the default sysctl net/ipv4/tcp_ecn
+ was changed from zero to '2', whereas the actual value
+ for ecn to be set is still '1'
+
+ * Allow different packet sizes for `--mtu'.
+ Suport `-l' option for tracepath wrapper.
+
+ * Some code and manual cleanups
+
+
+2010-12-14 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.17
+
+ * Adapt code to make possible the use of Linux capabilities
+ (for raw sockets etc.) instead of superuser privileges only.
+
+ On modern systems the capabilities can be stored as
+ file attributes, ie.:
+
+ "setcap cap_net_raw=pe /usr/bin/traceroute"
+
+
+2010-09-13 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.16
+
+ * A little work-around in the build system
+ for the new (buggy?) make 3.82
+
+ * Add `--fwmark=num' option for firewall mark (for kernel >= 2.6.25).
+ Idea comes from an anonymous SF patch #3042539
+
+
+2010-07-14 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.15
+
+ * Use string routines more safely (fix SF bug #3029216)
+
+ * Provide help for lft wrapper
+
+
+2010-04-21 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.14
+
+ * Fix support for IPv6's flow_labels and tclass.
+ Thanks to Peter Bieringer for testing
+
+ * Use route header "type 2" instead of deprecated "type 0"
+ for `-g' option for IPv6. The default value can be changed
+ by specifying a number in the place of the first `-g' address.
+
+
+2009-11-02 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.13
+
+ * Check for first_hop is not zero value (vladz@devzero.fr)
+
+ * Always fill unresolved IP address by its numeric interpretation,
+ even if getnameinfo(3) leaves it untouched (as it does for ipv6
+ in some glibc versions, whereas always fills for ipv4)
+
+ * Cosmetic changes for man page (Andreas Mohr)
+
+
+2008-09-15 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.12
+
+ * Use common recv_reply() routine for all modules which
+ do recvmsg(2) call. Method-specific things go to callbacks.
+
+ Pass to init methods pointer to datalen instead of the value.
+
+ * Implement ICMP Extension support (rfc4884), `-e' option.
+ Parse MPLS info (rfc4950) to be more readable (Kaj Niemi)
+
+ * Implement Path MTU Discovery (similar to tracepath(1)),
+ with `--mtu' option. Changed mtu is printed once in a form
+ of `F=NUM' at the first probe of a hop which requires
+ such mtu to be reached. (Actually, the correspond "frag needed"
+ icmp message is normally sent by the previous hop).
+
+ * Print the number of backward hops when it differs with forward,
+ by `--back' option. The backward hops is guessed by a technique
+ similar to tracepath(1), there is no reliable way to obtain
+ such info though.
+
+ * The optional second argument (packet_len) now is the full length
+ of the packet, including IP headers. (It is obvious enough due to
+ the nature of this feature, and this is the behaviour of the
+ original traceroute). Particular trace methods can ignore this
+ (fe. tcp), or increase it up to the minimal value (udp, icmp).
+ The actual packet's size is alvays reported in the output header.
+
+ * Add tracepath(1)/tracepath6(1) shell wrapper.
+
+ * Allow DEF_AF to be redefined at cmdline (Teran McKinney)
+
+ * Do not check the correctness of `sim_probes' value -- it is
+ unneeded at all. This also fixes a bug when a value of sim_probes
+ appears to be more than the total number of probes.
+ Reported by Milos Malik.
+
+ * Allow default UDP method to cross zero port boundary (Milos Malik).
+ It is a strange corner case, but traditional traceroute
+ behaves exactly so.
+
+
+2008-04-25 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.11
+
+ * Use new pmtudisc value "probe" instead of "do" for `-F' option
+ (available since the kernel 2.6.22).
+
+ For kernels before 2.6.22, the `-F' (dontfragment) option
+ seems completely useless for IPv6 and partially useful
+ for IPv4 (when a user can flush routing caches some way).
+
+ * Fix installation in build system (Mike Frysinger)
+
+ * Don't compute checksum for ipv6 icmp packets ourselves,
+ the kernel overwrites it anyway by the proper values.
+
+ * Don't use explicit path to traceroute in wrapper scripts
+
+
+2008-04-17 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.10
+
+ * raw_can_connect(): ipv6 connected raw sockets
+ receive MSG_ERRQUEUE properly only for kernels >= 2.6.25
+
+ * remove useless "host" parameter for init methods
+
+ * add probe_by_seq() and probe_by_sk() routines,
+ don't pass whole probes' pointer to recv_probe method
+
+ * collect all sends in do_send() routine
+
+ * Interpret ENOBUFS errors for send(2) as "can retry later".
+
+ Slow devices (like ppp) with small tx_queue_len can reject
+ the sending of too many packets simultaneously. To handle this,
+ do_send() now returns a negate value in a case of ENOBUFS
+ and similar (instead of program exit). The send_probe method
+ clears the probe and returns immediately in such cases.
+ Then, if there is an amount of time to wait for something,
+ the failed probe will be attempted again after that time expired.
+ If nothing to wait more, the program is exited.
+
+
+2007-09-26 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.9
+
+ * Complete manual page.
+
+ * Edit manual page to sound more English, thanks to Chris Ward
+
+
+2007-09-04 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.8
+
+ * Move all wrappers to special "wrappers/" dir.
+ Add lft(8) shell wrapper.
+ Add traceproto(8) shell wrapper.
+ Add traceroute-nanog(8) shell wrapper.
+
+ * Interpret first_hop as number, not index
+
+ * Build system is re-worked to match more the modern requirements
+ (Thanks to Mike Frysinger for testing).
+
+ * Check for kernel version >= 2.6.22.2 in raw_can_connect()
+
+ * Add generic "raw" method, "-P protonum" option.
+ New "one_per_time" flag for tr_module.
+
+
+2007-07-31 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.7
+
+ * Fix revents checking typo
+
+ * Expect normal data reply from udp too.
+
+ * Implement udp to port (-U) and udplite (-UL) methods.
+ Both available for unprivileged users.
+ Add "coverage" option for udplite.
+
+ * Allow non-digit service names for `-p' and `--sport'
+
+ * Drop period at the end of "SEE ALSO" section, and
+ avoid specific distro names in the manual (Mike Frysinger)
+
+ * Explicitly mention that this program is licensed
+ as "GPL version 2 or any later version".
+ (Similar for libsupp subdir: LGPL version 2.1 or any later).
+
+ * Always check whether the dest and source port match in
+ received packets. Can decrease an amount of (hypothetical)
+ garbage received just after the bind() but before connect()
+
+
+2007-07-19 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.6
+
+ * Rename tr_ops to tr_module
+
+ * Implement module-specific options (-O opt,...)
+
+ * Add TCP specific options (all the tcp header flags,
+ ecn, sack, timestamps, window_scaling, mss, sysctl)
+ Build tcp probe packet depending on them.
+
+ * Add "--sport" option for explicit source port selection.
+ Always cause "-N 1" when it is set.
+
+ * Add new routine bind_socket().
+ Always (auto)bind sockets in tune_socket().
+
+ * Add tcptraceroute(8) shell wrapper
+
+
+2007-07-16 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.5
+
+ * Use MSG_ERRQUEUE for raw sockets too.
+
+ * raw_can_connect () work-around for kernel bug #8747
+
+ * random.c, csum.c: new separate files
+
+ * New implementation of tcp method ("-T"), using
+ half-open technique. The old implementation module
+ renamed to "tcpconn" ("-M tcpconn").
+
+ * Common parse_cmsg() routine
+
+ * put ee_info for parse_icmp_res() too,
+ handle ICMP6_PACKET_TOO_BIG for IPv6,
+ report "!F-num" when "frag needed" (legacy compatibility)
+
+
+2007-07-11 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.4
+
+ * clear includes of unneeded headers
+
+ * move poll stuff to separate poll.c
+
+ * add module stuff (module.c), options etc.
+ Adapt udp/icmp/tcp for this.
+
+ * Add common routines use_recverr() and set_ttl()
+
+
+2007-02-28 Dmitry Butskoy <Dmitry@Butskoy.name>
+
+ * fix variable type for getsockname (Mike Frysinger)
+
+
+2007-01-09 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.3
+
+ * version 2.0.3
+
+ * allow option args without separator (add CLIF_MAY_JOIN_ARG flag),
+ for compatibility (Benjamin LaHaise)
+
+ * no more "tcptraceroute" symlink for rpm packages, because
+ it conflicts with the same-name old package anyway (James Ralston)
+
+ * fix compilation on glibc < 2.4 (Andy Shevchenko)
+
+
+2006-10-30 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.2
+
+ * version 2.0.2
+
+ * More accurate check_expired() routine.
+
+ * Some minor fixes.
+
+ * Add NOTES section to manual
+
+
+2006-10-20 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.1
+
+ * version 2.0.1
+
+ * Now ops methods write send_time (as well as recv_time)
+
+ * Use SO_TIMESTAMP to obtain msecs precisely
+
+ * Complete manual
+
diff --git a/Make.defines b/Make.defines
new file mode 100644
index 0000000..185db54
--- /dev/null
+++ b/Make.defines
@@ -0,0 +1,55 @@
+# The name of this distributive (used for tarballs etc.).
+# *MUST* be defined here...
+#
+NAME = traceroute
+
+# All subdir lists below are space-separated, with make's '%' wildcard
+# allowed. Default value is shown in commented out example.
+#
+
+# Subdirs to exclude anyway
+#
+#SKIPDIRS = tmp%
+
+# Subdirs to be treated as libdirs.
+#
+#LIBDIRS = lib%
+
+# An exact non-wildcard list of libdirs to be placed last in LIBDIRS list,
+# with the order specified. Useful, when some library should be
+# specified last because of referencing from another library...
+#
+#LIBLAST =
+
+# Subdirs to be treated as includedirs.
+# Also all LIBDIRS will be actually used for includes too.
+#
+#INCLUDEDIRS = include%
+
+# Subdirs to be treated as moduledirs.
+#
+#MODDIRS = mod%
+
+# Subdirs of executables which will use modules at run-time linking.
+#
+#MODUSERS =
+
+# Subdirs of executables to be installed in `*/sbin' instead of `*/bin'.
+#
+#SBINUSERS =
+
+# Subdirs to exclude from install
+#
+SKIPINSTALL = test% libsupp
+
+
+# A better way to alter the variables is to use "+=" or
+# "override VAR += value"
+#
+# CFLAGS +=
+# CPPFLAGS +=
+# LDFLAGS +=
+# LIBS +=
+
+CPPFLAGS += -D_GNU_SOURCE
+
diff --git a/Make.rules b/Make.rules
new file mode 100644
index 0000000..f1ddc25
--- /dev/null
+++ b/Make.rules
@@ -0,0 +1,213 @@
+#
+# Copyright (c) 2000, 2001, 2007 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Common rule and variable definitions.
+# This file should be included by main and by per-target Makefiles.
+#
+
+
+ifndef srcdir
+$(error srcdir variable not defined)
+endif
+
+
+MAKE = make --no-print-directory -r
+
+
+# Use env=yes on cmdline to inherit environment values
+
+ifeq ($(env),yes)
+define set
+$(eval ifneq ($$(origin $(1)),environment)
+$(1) = $(2)
+else
+override MAKE := $(1)="$($(strip $(1)))" $(MAKE)
+endif)
+endef
+else
+set = $(eval $(1) = $(2))
+endif
+
+
+$(call set, CROSS, )
+$(call set, CC, $$(CROSS)gcc)
+$(call set, AS, $$(CROSS)as)
+$(call set, LD, $$(CROSS)ld)
+$(call set, DEPEND, $$(CROSS)gcc -MM -MG)
+$(call set, AR, $$(CROSS)ar)
+$(call set, RANLIB, $$(CROSS)ranlib)
+$(call set, INSTALL, cp)
+$(call set, INDENT, true)
+
+gcc = $(findstring gcc,$(CC))
+$(call set, CFLAGS, $(if $(gcc), -O2 -Wall, -O))
+$(call set, CPPFLAGS, )
+$(call set, LDFLAGS, -s)
+$(call set, LIBS, )
+
+
+# install stuff
+prefix = /usr/local
+
+ifneq ($(wildcard /lib64/libc.* /usr/lib64/libc.*),)
+lib := lib64
+else
+lib := lib
+endif
+
+exec_prefix = $(prefix)
+bindir = $(exec_prefix)/bin
+sbindir = $(exec_prefix)/sbin
+libdir = $(exec_prefix)/$(lib)
+libexecdir = $(exec_prefix)/libexec/$(NAME)
+sysconfdir = $(prefix)/etc
+includedir = $(prefix)/include
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+infodir = $(datadir)/info
+localstatedir = $(prefix)/var
+sharedstatedir = $(prefix)/com
+
+DESTDIR =
+
+
+# layout stuff
+SKIPDIRS = tmp%
+INCLUDEDIRS = include%
+LIBDIRS = lib%
+MODDIRS = mod%
+SKIPINSTALL = test%
+
+
+include $(srcdir)/Make.defines
+
+
+ifndef NAME
+NAME = $(notdir $(srcdir))
+endif
+
+
+ifndef subdirs
+ifeq ($(TARGET),.MAIN)
+# for better performance...
+subdirs := $(patsubst %/,%,$(wildcard */))
+else
+subdirs := $(patsubst $(srcdir)/%/,%,$(filter %/,$(wildcard $(srcdir)/*/)))
+endif
+subdirs := $(filter-out $(SKIPDIRS), $(subdirs))
+endif
+
+install install-%: subdirs := $(filter-out $(SKIPINSTALL), $(subdirs))
+
+
+override MAKE += srcdir=$(srcdir) subdirs="$(subdirs)" shared=$(shared)
+
+
+INCLUDEDIRS := $(filter $(INCLUDEDIRS), $(subdirs))
+LIBDIRS := $(filter $(LIBDIRS), $(subdirs))
+MODDIRS := $(filter $(MODDIRS), $(subdirs))
+EXEDIRS := $(filter-out $(INCLUDEDIRS) $(LIBDIRS) $(MODDIRS), $(subdirs))
+MODUSERS := $(filter $(MODUSERS), $(subdirs))
+SBINUSERS := $(filter $(SBINUSERS), $(subdirs))
+
+LIBDIRS := $(filter-out $(LIBLAST),$(LIBDIRS)) $(filter $(LIBDIRS),$(LIBLAST))
+
+
+includes = $(foreach dir, $(INCLUDEDIRS) $(LIBDIRS), $(srcdir)/$(dir))
+
+ifneq ($(gcc),)
+CPATH = $(subst $(empty) ,:,$(includes))
+export CPATH
+else
+override CPPFLAGS += $(patsubst %,-I%,$(includes))
+endif
+
+libraries = $(foreach dir, $(filter lib%,$(LIBDIRS)), $(srcdir)/$(dir))
+
+vpath lib%.so $(libraries)
+vpath lib%.a $(libraries)
+
+_libs = $(strip $(foreach _lib,$(LIBDIRS),\
+ $(if $(filter lib%,$(_lib)),\
+ $(patsubst lib%,-l%,$(_lib)),\
+ $(wildcard $(srcdir)/$(_lib)/$(_lib).so \
+ $(srcdir)/$(_lib)/$(_lib).a))))
+
+override LIBS := $(_libs) -lm $(LIBS)
+
+ifeq ($(CC),gcc)
+LIBRARY_PATH = $(subst $(empty) ,:,$(libraries))
+export LIBRARY_PATH
+else
+override LIBS += $(patsubst %,-L%,$(libraries))
+endif
+
+LIBDEPS = $(filter-out -L%,$(LIBS))
+
+
+#
+# SUBDIRS STUFF
+#
+
+ifneq ($(TARGET),.MAIN)
+
+obj := o
+ifeq ($(shared),yes)
+ifneq ($(PIC),no)
+ifeq ($(filter $(TARGET),$(LIBDIRS) $(MODDIRS) .MODULE),$(TARGET))
+obj := lo
+endif
+endif
+endif
+
+sources = $(wildcard *.c)
+OBJS = $(sources:.c=.$(obj))
+
+
+.PHONY: dummy all depend install clean force
+
+dummy: all
+
+
+clean:
+ rm -f *.o *.a *.lo *.so $(TARGET) core a.out
+
+
+ifneq ($(sources),)
+depend: $(sources)
+ $(DEPEND) $(CFLAGS) $(CPPFLAGS) $^ >.depend
+else
+depend:
+ @true
+endif
+
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $*.c
+
+%.lo: %.c
+ $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -o $*.lo -c $*.c
+
+%.o: %.s
+ $(AS) -o $*.o $*.s
+
+%.o: %.S
+ $(CC) -traditional $(CPPFLAGS) -c $*.S
+
+# include if it is present only...
+ifeq (.depend, $(wildcard .depend))
+include .depend
+endif
+
+endif
+
+#
+# ...end of SUBDIRS STUFF
+#
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e302c2b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,117 @@
+#
+# Copyright (c) 2000, 2001 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Global Makefile.
+# Global rules, targets etc.
+#
+# See Make.defines for specific configs.
+#
+
+
+srcdir = $(CURDIR)
+
+override TARGET := .MAIN
+
+dummy: all
+
+include ./Make.rules
+
+
+targets = $(EXEDIRS) $(LIBDIRS) $(MODDIRS)
+
+
+# be happy, easy, perfomancy...
+.PHONY: $(subdirs) dummy all force
+.PHONY: depend indent clean distclean libclean release store libs mods
+
+
+allprereq := $(EXEDIRS)
+
+ifneq ($(LIBDIRS),)
+libs: $(LIBDIRS)
+ifneq ($(EXEDIRS),)
+$(EXEDIRS): libs
+else
+allprereq += libs
+endif
+endif
+
+ifneq ($(MODDIRS),)
+mods: $(MODDIRS)
+ifneq ($(MODUSERS),)
+$(MODUSERS): mods
+else
+allprereq += mods
+endif
+ifneq ($(LIBDIRS),)
+$(MODDIRS): libs
+endif
+endif
+
+all: $(allprereq)
+
+depend install: $(allprereq)
+
+$(foreach goal,$(filter install-%,$(MAKECMDGOALS)),\
+ $(eval $(goal): $(patsubst install-%,%,$(goal))))
+
+
+what = all
+depend: what = depend
+install install-%: what = install
+
+ifneq ($(share),)
+$(share): shared = yes
+endif
+ifneq ($(noshare),)
+$(noshare): shared =
+endif
+
+
+$(targets): mkfile = $(if $(wildcard $@/Makefile),,-f $(srcdir)/default.rules)
+
+$(targets): force
+ @$(MAKE) $(mkfile) -C $@ $(what) TARGET=$@
+
+force:
+
+
+indent:
+ find . -type f -name "*.[ch]" -print -exec $(INDENT) {} \;
+
+clean:
+ rm -f $(foreach exe, $(EXEDIRS), ./$(exe)/$(exe)) nohup.out
+ rm -f `find . \( -name "*.[oa]" -o -name "*.[ls]o" \
+ -o -name core -o -name "core.[0-9]*" -o -name a.out \) -print`
+
+distclean: clean
+ rm -f `find $(foreach dir, $(subdirs), $(dir)/.) \
+ \( -name "*.[oa]" -o -name "*.[ls]o" \
+ -o -name core -o -name "core.[0-9]*" -o -name a.out \
+ -o -name .depend -o -name "_*" -o -name ".cross:*" \) \
+ -print`
+
+
+libclean:
+ rm -f $(foreach lib, $(LIBDIRS), ./$(lib)/$(lib).a ./$(lib)/$(lib).so)
+
+
+# Rules to make whole-distributive operations.
+#
+
+STORE_DIR = $(HOME)/pub
+
+release release1 release2 release3:
+ @./chvers.sh $@
+ @$(MAKE) store
+
+store: distclean
+ @./store.sh $(NAME) $(STORE_DIR)
+
+
diff --git a/README b/README
new file mode 100644
index 0000000..8f25a7f
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+This is a new modern implementation of the traceroute(8)
+utility for Linux systems.
+
+Traceroute tracks the route packets taken from an IP network on their
+way to a given host. It utilizes the IP protocol's time to live (TTL)
+field and attempts to elicit an ICMP TIME_EXCEEDED response from each
+gateway along the path to the host.
+
+Main features:
+- Full support for both IPv4 and IPv6 protocols
+- Several tracerouting methods, including:
+ * UDP datagrams (including udplite and udp to particlular port)
+ * ICMP ECHO packets (including dgram icmp sockets)
+ * TCP SYNs (in general, any TCP request with various flags and options)
+ * DCCP Request packets
+ * Generic IP datagrams
+- UDP methods do not require root privileges
+- Ability to send several probe packets at a time
+- Ability to compute a proper time to wait for each probe
+- perform AS path lookups for returned addresses
+- show ICMP extensions, including MPLS
+- perform path MTU discovery automatically
+- show guessed number of hops in backward direction
+- command line compatible with the original traceroute
+- and much more, see traceroute(8)
+
+This code was written from the scratch, using some ideas of
+Olaf Kirch's traceroute, the original implementation of Van Jacobson
+(which was long used before) and some current BSD's ones.
+
+This traceroute requires Linux kernel 2.6 and higher.
+
+You can try to contact the author at <Dmitry at Butskoy dot name> .
+
+
+Good tracerouting!
+
+Dmitry Butskoy
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..bdd878e
--- /dev/null
+++ b/TODO
@@ -0,0 +1,33 @@
+* It seems that `-l' and `-g' will not work correctly together (IPv6).
+
+ Wait for "flow label" API to be appeared in kernel-headers and
+ think about it immediately after that... :)
+
+
+* The "final hop" issue.
+
+ All methods, usable when firewalls are present in the network path,
+ normally use some particular destination port. Most often it is a port
+ of an already running application.
+
+ It requires that the packet sent should be correct for such an application
+ (for example, for tracing with udp to port 53, it should be correct DNS
+ request), and the application normally should answer something on it.
+ (TCP has no such an issue, as there are just syn to, and ack or reset from).
+
+ In general, we should fill the packet's data depending on the dest port
+ and protocol. It seems not a task for traceroute itself, it could be
+ some cmdline option or even external hook...
+
+
+* Think about SCTP method.
+
+
+* Think about "multicast tracerouting" (mrouted(8) and other).
+ The idea is to increase the room in the mtrace packet step-by-step
+ (as well as we increase ttl). It seems that if there is no more space
+ in the probe's room, the mrouted(8) daemon answers immediately, the same
+ way as if it is a final hop.
+
+ For IPv6 mtrace, there is an RFC draft for this already...
+
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..374780e
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+#define VERSION 2.1.5
diff --git a/chvers.sh b/chvers.sh
new file mode 100755
index 0000000..8818d7d
--- /dev/null
+++ b/chvers.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# Copyright (c) 2000, 2001 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Change version script.
+# Normally invoked by a Makefile.
+#
+# Changes version info in directory name, in VERSION file
+# and rpm spec file (if some of these are used).
+#
+# `release3' changes: 0.2.7 --> 0.2.8
+# `release2' changes: 0.2.7 --> 0.3.0
+# `release1' changes: 0.2.7 --> 1.0.0
+# etc.
+# `release' without a digit increment the last number used.
+#
+#
+
+
+[ $# -lt 1 ] && {
+ echo "Usage: $0 release[123...0]" >&2
+ exit 2
+}
+
+level=`expr $1 : '.*\([0-9]\)$'`
+[ -z "$level" ] && level=0
+
+main_dir=`basename \`pwd\``
+
+
+# Find current version info.
+
+dir_v=""
+file_v=""
+
+dir_v=`expr $main_dir : '.*-\([0-9.]*\)$'`
+[ -r VERSION ] && file_v=`awk '/^ *#/ { print $3 ; exit; }
+ /^ *VERSION *=/ {split ($0, a, "="); print a[2]; exit; }
+ { print $0; exit; }' < VERSION `
+[ -n "$file_v" ] && file_v=`echo $file_v ` # to strip possible spaces
+
+[ -z "$file_v" -a -z "$dir_v" ] && {
+ echo "$0: Cannot determine version (use dirname postfix or VERSION file)" >&2
+ exit 2
+}
+
+[ -n "$file_v" -a -n "$dir_v" -a "$dir_v" != "$file_v" ] && {
+ echo "$0: Different version from dirname postfix and VERSION file" >&2
+ exit 2
+}
+
+version="$dir_v"
+[ -z "$version" ] && version="$file_v"
+
+
+# Increment current version, as specified by level.
+
+new_version=`echo $version | awk '{
+ level = '"$level"';
+ n = split ($0, a, ".");
+ if (level == 0) level = n;
+ for (i = 1; i <= n; i++) {
+ if (i == level) a[i] = a[i] + 1 ;
+ else if (i > level) a[i] = 0 ;
+ }
+ str = a[1]
+ for (i = 2; i <= n; i++) str = str "." a[i]
+ print str
+}' 2>/dev/null `
+
+
+# Adjust VERSION file, if any.
+
+# it is ugly, because $version contains dots...
+[ -n "$file_v" ] && {
+ sed "s/$version/$new_version/" < VERSION > VERSION.new && mv -f VERSION.new VERSION
+}
+
+# Adjust rpm .spec file, if any.
+for spec in *.spec
+do
+ [ -f $spec ] || continue
+
+ grep '^Version:[ ]*'"$version" $spec >/dev/null 2>&1 || continue
+ sed '/^Version:[ ]*'"$version/ s/$version/$new_version/" < $spec > ${spec}.new && mv -f ${spec}.new $spec
+done
+
+
+# Adjust dirname postfix, if any.
+
+[ -n "$dir_v" ] && {
+ base=`expr \`pwd\` : '^\(.*\)-'"$version"'$' `
+ mv -f ${base}-$version ${base}-$new_version
+}
+
+exit 0
diff --git a/default.rules b/default.rules
new file mode 100644
index 0000000..9b6c822
--- /dev/null
+++ b/default.rules
@@ -0,0 +1,193 @@
+#
+# Copyright (c) 2000, 2001, 2007 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Default a target`s Makefile. Useful for any things...
+# For some changes, copy it to a dir where needed and edit that copy.
+#
+
+
+ifndef srcdir
+rul := Make.rules
+path := $(word 1, $(wildcard ../$(rul) ../../$(rul)))
+ifeq ($(path),../$(rul))
+srcdir = $(dir $(CURDIR))
+endif
+ifeq ($(path),../../$(rul))
+srcdir = $(dir $(patsubst %/,%,$(dir $(CURDIR))))
+TARGET = .MODULE
+endif
+ifeq ($(srcdir),)
+$(error Cannot find srcdir (where $(rul) exists))
+endif
+endif
+
+ifndef TARGET
+TARGET = $(notdir $(CURDIR))
+endif
+
+include $(srcdir)/Make.rules
+
+
+#
+# LIBDIRS DEFAULTS
+#
+ifeq ($(filter $(TARGET),$(LIBDIRS)),$(TARGET))
+
+ifeq ($(shared),yes)
+
+all: $(TARGET).so
+
+$(TARGET).so: $(OBJS)
+ $(CC) -shared -o $@ -Wl,-soname -Wl,$@ $(OBJS)
+
+else
+
+all: $(TARGET).a
+
+$(TARGET).a: $(OBJS)
+ rm -f $@
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+endif
+
+install_what = $(wildcard $(TARGET).so $(TARGET).a)
+install_dir = $(libdir)
+install_includes = $(wildcard *.h)
+
+cross_stamp_file = $(wildcard .cross:*)
+ifeq ($(cross_stamp_file),)
+cross_stamp = .cross:
+else
+cross_stamp = $(cross_stamp_file)
+endif
+new_stamp = .cross:$(subst $(empty) ,:,$(CROSS))
+ifneq ($(cross_stamp),$(new_stamp))
+
+$(OBJS): force
+
+force:
+ rm -f $(cross_stamp) > $(new_stamp)
+ @rm -f *.o *.a *.lo *.so
+
+endif
+
+endif
+
+
+#
+# MODDIRS DEFAULTS
+#
+ifeq ($(filter $(TARGET),$(MODDIRS)),$(TARGET))
+
+modules = $(filter-out $(SKIPDIRS), $(patsubst %/,%,$(wildcard */)))
+
+.PHONY: $(modules)
+
+what = all
+depend: what = depend
+clean: what = clean
+
+all depend clean: $(modules)
+
+$(modules): mkfile = $(if $(wildcard $@/Makefile),,-f $(srcdir)/default.rules)
+
+$(modules): force
+ @$(MAKE) $(mkfile) -C $@ $(what) TARGET=.MODULE MODULE=$@
+
+force:
+
+install_what = $(wildcard *.so)
+install_dir = $(libexecdir)
+
+endif
+
+
+#
+# MODDIRS` subdirectories (i.e., modules) defaults
+#
+ifeq ($(TARGET),.MODULE)
+
+ifndef MODULE
+MODULE = $(notdir $(CURDIR))
+endif
+
+ifeq ($(shared),yes)
+
+all: ../$(MODULE).so
+
+../$(MODULE).so: $(OBJS) $(LIBDEPS)
+ $(CC) -shared -o $@ $(OBJS) $(LIBS)
+
+else
+
+all: ../$(MODULE).o
+
+../$(MODULE).o: $(OBJS)
+ $(LD) -r -o $@ $(OBJS)
+
+endif
+
+endif
+
+
+#
+# EXEDIRS DEFAULTS (for usual executables)
+#
+ifeq ($(filter $(TARGET),$(EXEDIRS)),$(TARGET))
+
+ifeq ($(filter $(TARGET),$(MODUSERS)),$(TARGET))
+MOD_OBJS = $(wildcard $(foreach dir,$(MODDIRS),$(srcdir)/$(dir)/*.o))
+ifeq ($(shared),yes)
+override LDFLAGS := -rdynamic $(LDFLAGS)
+endif
+install_includes= $(wildcard $(foreach dir,$(INCLUDEDIRS),$(srcdir)/$(dir)/*.h))
+install_includes:= $(filter-out $(srcdir)/include/version.h,$(install_includes))
+else
+MOD_OBJS =
+endif
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS) $(MOD_OBJS) $(LIBDEPS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(MOD_OBJS) $(LIBS)
+
+install_what = $(wildcard $(TARGET))
+install_dir = $(if $(filter $(TARGET),$(SBINUSERS)),$(sbindir),$(bindir))
+
+endif
+
+
+#
+# Install stuff
+#
+
+install_manuals = $(wildcard *.[0-9] *.[0-9]?)
+
+
+install:
+ifneq ($(install_dir),)
+ @mkdir -p $(DESTDIR)$(install_dir)
+endif
+ifneq ($(install_what),)
+ $(INSTALL) $(install_what) $(DESTDIR)$(install_dir)
+endif
+ifneq ($(install_includes),)
+ @mkdir -p $(DESTDIR)$(includedir)
+ $(INSTALL) $(install_includes) $(DESTDIR)$(includedir)
+endif
+ @true
+ifneq ($(install_manuals),)
+ $(foreach man,$(install_manuals),$(call inst_man,$(man)))
+define inst_man
+@mkdir -p $(DESTDIR)$(mandir)/man$(patsubst .%,%,$(suffix $(1)))
+ cp -f $(1) $(DESTDIR)$(mandir)/man$(patsubst .%,%,$(suffix $(1)))
+
+endef
+endif
diff --git a/include/version.h b/include/version.h
new file mode 100644
index 0000000..af1b427
--- /dev/null
+++ b/include/version.h
@@ -0,0 +1,9 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include "../VERSION"
diff --git a/libsupp/clif.c b/libsupp/clif.c
new file mode 100644
index 0000000..2c21326
--- /dev/null
+++ b/libsupp/clif.c
@@ -0,0 +1,1259 @@
+/*
+ Copyright (c) 2000, 2003 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: LGPL v2.1 or any later
+
+ See COPYING.LIB for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "clif.h"
+
+
+#if 1 /* Bad idea, anyway... */
+#define MAX_ARGC_NUMBER 256
+typedef unsigned char _CLIF_index;
+#else
+#define MAX_ARGC_NUMBER (4096 / 5 + 1) /* POSIX ARG_MAX >= 4096 ... */
+typedef unsigned short _CLIF_index;
+#endif
+
+
+/* This is needed for some print info functions.
+ This is ugly for thread-safe (is it really actual on program invoking?),
+ and for several CLIF_parse_cmdline invoking... But foo on this. Yeah...
+*/
+static struct {
+ int argc;
+ char **argv;
+ CLIF_option *option_list;
+ CLIF_argument *argument_list;
+ unsigned int parse_flags;
+} curr = { 0, };
+
+
+static void err_report (const char *format, ...) {
+ va_list ap;
+
+ if (curr.parse_flags & CLIF_SILENT)
+ return;
+
+ va_start (ap, format);
+
+ vfprintf (stderr, format, ap);
+
+ va_end (ap);
+
+ fprintf (stderr, "\n");
+
+ return;
+}
+
+
+/* info generation stuff... */
+
+#define SHORT_PLUS_MINUS "+/-"
+#define LONG_PLUS_MINUS "++/--"
+#define EXCL_DLM " | "
+
+static char *show_short (const CLIF_option *optn) {
+ static char buf[80];
+ char *p = buf;
+ unsigned int flags = optn->flags | curr.parse_flags;
+
+ if (optn->function_plus) {
+ if (!optn->function) *p++ = '+';
+ else {
+ strcpy (p, SHORT_PLUS_MINUS);
+ p += sizeof (SHORT_PLUS_MINUS) - 1;
+ }
+ } else
+ *p++ = '-';
+
+ *p++ = optn->short_opt[0];
+
+ if (optn->arg_name) {
+ char *endp = buf + sizeof (buf) - sizeof (",...]");
+ const char *s;
+
+ if (!(flags & _CLIF_STRICT_JOIN_ARG)) *p++ = ' ';
+ if (flags & CLIF_OPTARG) *p++ = '[';
+
+ s = optn->arg_name;
+ while (*s && p < endp) *p++ = *s++;
+
+ if (flags & CLIF_SEVERAL) {
+ strcpy (p, ",...");
+ p += sizeof (",...") - 1; /* last '\0' ... */
+ }
+
+ if (flags & CLIF_OPTARG) *p++ = ']';
+ }
+
+ *p = '\0';
+
+ return buf;
+}
+
+static char *show_long (const CLIF_option *optn) {
+ static char buf[80];
+ char *p = buf;
+ char *endp;
+ const char *s;
+ unsigned int flags = optn->flags | curr.parse_flags;
+
+
+ if (!(flags & _CLIF_STRICT_KEYWORD)) {
+
+ if (!(flags & _CLIF_STRICT_ONEDASH)) {
+ if (optn->function_plus) {
+ if (!optn->function) { *p++ = '+'; *p++ = '+'; }
+ else {
+ strcpy (p, LONG_PLUS_MINUS);
+ p += sizeof (LONG_PLUS_MINUS) - 1;
+ }
+ } else { *p++ = '-'; *p++ = '-'; }
+
+ } else {
+ if (optn->function_plus) {
+ if (!optn->function) *p++ = '+';
+ else {
+ strcpy (p, SHORT_PLUS_MINUS);
+ p += sizeof (SHORT_PLUS_MINUS) - 1;
+ }
+ } else *p++ = '-';
+ }
+ }
+
+ s = optn->long_opt;
+ endp = buf + sizeof (buf) - sizeof (" [");
+ while (*s && p < endp) *p++ = *s++;
+
+ if (optn->arg_name) {
+
+ if (flags & _CLIF_STRICT_NOEQUAL) {
+ *p++ = ' ';
+ if (flags & CLIF_OPTARG) *p++ = '[';
+ } else {
+ if (flags & CLIF_OPTARG) *p++ = '[';
+ *p++ = '=';
+ }
+
+ s = optn->arg_name;
+ endp = buf + sizeof (buf) - sizeof (",...]");
+ while (*s && p < endp) *p++ = *s++;
+
+ if (flags & CLIF_SEVERAL) {
+ strcpy (p, ",...");
+ p += sizeof (",...") - 1; /* last '\0' ... */
+ }
+
+ if (flags & CLIF_OPTARG) *p++ = ']';
+ }
+
+ *p = '\0';
+
+ return buf;
+}
+
+static char *show_excl (const CLIF_option *option_list, int *cnt_p) {
+ static char buf[256];
+ const CLIF_option *optn;
+ char *p = buf;
+ char *endp = buf + sizeof (buf) - sizeof (EXCL_DLM);
+ int excl_cnt = 0;
+
+ *p = '\0';
+ if (cnt_p) *cnt_p = 0;
+ if (!option_list) return buf;
+
+ for (optn = option_list; optn->short_opt || optn->long_opt; optn++) {
+ char *s;
+
+ if (!(optn->flags & CLIF_EXCL)) continue;
+
+ if (optn->short_opt) s = show_short (optn);
+ else s = show_long (optn);
+
+ if (excl_cnt > 0) { /* i.e., second etc... */
+ strcpy (p, EXCL_DLM);
+ p += sizeof (EXCL_DLM) - 1;
+ }
+
+ while (*s && p < endp) *p++ = *s++;
+
+ excl_cnt++;
+ }
+
+ *p = '\0';
+
+ if (cnt_p) *cnt_p = excl_cnt;
+
+ return buf;
+}
+
+
+static int is_keyword (const CLIF_option *optn) {
+ unsigned int flags = optn->flags | curr.parse_flags;
+
+ return (flags & _CLIF_STRICT_KEYWORD) != 0;
+}
+
+
+static void err_bad_opt (const char *arg, char c, int n) {
+ char sym = (*arg == '+') ? '+' : '-';
+
+ if (c) err_report ("Bad option `%c%c' (argc %d)", sym, c, n);
+ else {
+ char *p = strchr (arg, '=');
+ const char *type = (*arg == sym) ? "option" : "keyword";
+
+ if (p)
+ err_report ("Bad %s `%s' (with arg `%s') (argc %d)",
+ type, arg, p + 1, n);
+ else
+ err_report ("Bad %s `%s' (argc %d)", type, arg, n);
+ }
+}
+
+static void err_bad_arg (const CLIF_option *optn, char c, int n) {
+ CLIF_option tmp = *optn;
+ char ss[80];
+ char *s;
+
+ tmp.arg_name = NULL;
+
+ if (c) {
+ s = show_short (&tmp); /* always without arg... */
+ strncpy (ss, s, sizeof (ss));
+ s = show_short (optn);
+ } else {
+ s = show_long (&tmp); /* always without arg... */
+ strncpy (ss, s, sizeof (ss));
+ s = show_long (optn);
+ }
+
+ err_report ("%s `%s' (argc %d) requires an argument: `%s'",
+ (c || !is_keyword (optn)) ? "Option" : "Keyword", ss, n, s);
+}
+
+static void err_bad_res (const CLIF_option *optn, char c,
+ const char *opt_arg, int n) {
+ CLIF_option tmp = *optn;
+ char *ss;
+ const char *type;
+
+ tmp.arg_name = NULL;
+
+ if (c) {
+ ss = show_short (&tmp);
+ type = "option";
+ } else {
+ ss = show_long (&tmp);
+ type = is_keyword (optn) ? "keyword" : "option";
+ }
+
+ if (optn->arg_name)
+ err_report ("Cannot handle `%s' %s with arg `%s' (argc %d)",
+ ss, type, opt_arg, n);
+ else
+ err_report ("Cannot handle `%s' %s (argc %d)", ss, type, n);
+}
+
+static void err_bad_excl (const CLIF_option *optn, char c, int n) {
+ CLIF_option tmp = *optn;
+ char *ss;
+ char *excl = show_excl (curr.option_list, 0);
+ /* Note: show_(short|long)() nested!!! */
+
+ tmp.arg_name = NULL;
+
+ if (c) ss = show_short (&tmp);
+ else ss = show_long (&tmp);
+
+ err_report ("%s `%s' (argc %d): Only one of:\n %s\n"
+ "may be specified.",
+ (c || !is_keyword (optn)) ? "Option" : "Keyword",
+ ss, n, excl);
+}
+
+
+static CLIF_option *find_long (char *arg, char **arg_p,
+ unsigned int match, unsigned int nomatch) {
+ CLIF_option *optn;
+ CLIF_option *abbrev = NULL;
+ char *abbrev_arg = NULL;
+ int abbrev_found = 0;
+
+
+ for (optn = curr.option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ char *a;
+ const char *o;
+ unsigned int flags;
+
+ if (!optn->long_opt) continue;
+
+ flags = curr.parse_flags | optn->flags;
+ if (flags & nomatch) continue;
+ if (match && !(flags & match)) continue; /* XXX: optimize it */
+
+
+ for (a = arg, o = optn->long_opt; *o && *a == *o; a++, o++) ;
+
+ if (*a == '\0' ||
+ (*a == '=' && optn->arg_name && !(flags & _CLIF_STRICT_NOEQUAL))
+ ) { /* looks like end of option... */
+
+ if (!*o) { /* explicit match found */
+ if (*a == '=' && arg_p) *arg_p = a + 1;
+ return optn;
+ }
+
+ if ((flags & CLIF_ABBREV) &&
+ (a - arg >= CLIF_MIN_ABBREV)
+ ) {
+ if (!abbrev_found) {
+ abbrev_found = 1;
+ abbrev = optn;
+ if (*a == '=') abbrev_arg = a + 1;
+ } else /* several possibility case... */
+ abbrev = NULL;
+ }
+ }
+ }
+
+ if (abbrev) { /* implicit match found */
+ if (abbrev_arg && arg_p) *arg_p = abbrev_arg;
+ return abbrev;
+ } else /* no match found */
+ return NULL;
+}
+
+static int check_sym (const CLIF_option *optn, char sym) {
+
+ if (sym == '+') {
+ if (!optn->function_plus) return -1;
+ }
+ else if (sym == '-') {
+ if (!optn->function && optn->function_plus)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int call_function (CLIF_option *optn, char *opt_arg, char sym) {
+ int (*function) (CLIF_option *, char *);
+
+ function = (sym == '+') ? optn->function_plus : optn->function;
+
+ if (!function) return 0;
+
+ if (opt_arg && ((optn->flags | curr.parse_flags) & CLIF_SEVERAL)) {
+ char tmp[80];
+ char *t;
+ char *endt = tmp + sizeof (tmp);
+
+ while (*opt_arg) {
+
+ t = tmp;
+ while (t < endt && *opt_arg &&
+ *opt_arg != ' ' && *opt_arg != '\t' && *opt_arg != ','
+ ) *t++ = *opt_arg++;
+
+ if (t >= endt) return -1;
+
+ *t = '\0';
+
+ if (function (optn, tmp) < 0) return -1;
+
+ while (*opt_arg == ' ' || *opt_arg == '\t' || *opt_arg == ',')
+ opt_arg++;
+ }
+
+ return 0;
+ }
+
+ return function (optn, opt_arg);
+}
+
+
+int CLIF_parse_cmdline (int argc, char *argv[],
+ CLIF_option *option_list,
+ CLIF_argument *argument_list,
+ unsigned int parse_flags) {
+ int i, j;
+ CLIF_option *optn;
+ CLIF_argument *argm;
+ int num_args = 0;
+ int num_argm = 0, strict_beg = 0, strict_end = 0;
+ _CLIF_index arg_n[MAX_ARGC_NUMBER];
+ unsigned int dirty_flags = 0;
+ int dirty_plus = 0;
+ int exclusive_cnt = 0;
+ int posix = getenv ("POSIXLY_CORRECT") != NULL ||
+ (parse_flags & CLIF_POSIX);
+
+ curr.argc = argc;
+ curr.argv = argv;
+ curr.option_list = option_list;
+ curr.argument_list = argument_list;
+ curr.parse_flags = parse_flags;
+
+ if (argc <= 1 && (parse_flags & CLIF_HELP_EMPTY)) {
+ CLIF_current_help ();
+ exit (0);
+ }
+
+ /* Scan argument_list for check and some info. */
+
+ if (argument_list) {
+ enum stages { STRICT_BEG, OPTIONAL, STRICT_END };
+ int stage = STRICT_BEG;
+
+ for (argm = argument_list; argm->name; argm++) {
+
+ if (argm->flags & CLIF_STRICT) {
+
+ if (stage == STRICT_BEG) strict_beg++;
+ else if (stage == OPTIONAL) {
+ stage = STRICT_END;
+ strict_end++;
+ }
+ else if (stage == STRICT_END)
+ strict_end++;
+ } else {
+ if (stage == STRICT_BEG) stage = OPTIONAL;
+ else if (stage == STRICT_END) {
+ err_report ("Incorrect argument list set in program "
+ "source: more than one optional area.");
+ return -1;
+ }
+ }
+
+ num_argm++;
+ }
+ }
+
+ /* Scan option_list for some info. */
+ if (option_list) {
+
+ dirty_flags = parse_flags;
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ dirty_flags |= optn->flags;
+ if (optn->function_plus) dirty_plus = 1;
+ }
+ }
+
+ if (dirty_flags & CLIF_EXCL)
+ exclusive_cnt = 1; /* only one is allowed... */
+
+
+ /* Go ! Store arguments, parse options. */
+
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ char *opt_arg = NULL;
+ char sym = '-';
+
+ if (!option_list)
+ goto handle_arg;
+
+ if (*arg == '+' && dirty_plus)
+ sym = '+';
+
+ if (*arg != sym) { /* argument or keyword */
+
+ if (dirty_flags & CLIF_MAY_KEYWORD) {
+ optn = find_long (arg, &opt_arg, CLIF_MAY_KEYWORD, 0);
+ if (optn) goto long_found;
+ }
+
+ if (num_args == 0 && (parse_flags & CLIF_FIRST_GROUP)) {
+ /* ugly... */
+ parse_flags &= ~CLIF_FIRST_GROUP;
+ dirty_flags &= ~CLIF_FIRST_GROUP; /* to be correct */
+
+ goto handle_short;
+ }
+
+ /* else it is an argument */
+ goto handle_arg;
+
+ }
+ else if (*++arg == sym) { /* `--' - long option */
+ arg++;
+
+ if (*arg == sym || /* `---' - let it be not option... */
+ (parse_flags & (_CLIF_STRICT_KEYWORD|_CLIF_STRICT_ONEDASH))
+ ) {
+ arg -= 2;
+ goto handle_arg; /* not option anyway */
+ }
+
+ optn = find_long (arg, &opt_arg, 0,
+ _CLIF_STRICT_KEYWORD | _CLIF_STRICT_ONEDASH);
+ if (optn) goto long_found;
+
+ /* XXX: May be allow only for `--', not `++' too... */
+ if (!*arg && sym == '-') { /* `--' and no empty longoption */
+ option_list = NULL; /* POSIX way... */
+ continue;
+ }
+
+ /* XXX: or treat as an argument sometimes??? */
+ err_bad_opt (argv[i], 0, i);
+ return -1;
+ }
+ else { /* short option, or several short options... */
+
+ if (dirty_flags & CLIF_MAY_ONEDASH) {
+ optn = find_long (arg, &opt_arg, CLIF_MAY_ONEDASH, 0);
+ if (optn) goto long_found;
+ }
+
+ if (!*arg) { /* POSIX say: only "stdout specification"... */
+ arg--;
+ goto handle_arg;
+ }
+
+ goto handle_short;
+ }
+
+
+ long_found:
+ if (check_sym (optn, sym) < 0) { /* Oops... */
+ err_bad_opt (argv[i], 0, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXCL) {
+ if (!exclusive_cnt) {
+ err_bad_excl (optn, 0, i);
+ return -1;
+ }
+ exclusive_cnt--;
+ }
+
+ if (optn->arg_name && !opt_arg) {
+ unsigned int flags = optn->flags | parse_flags;
+
+ if (++i >= argc ||
+ !(flags & CLIF_MAY_NOEQUAL)
+ ) { /* missing opt arg */
+ i--;
+
+ if (!(flags & CLIF_OPTARG)) {
+ err_bad_arg (optn, 0, i);
+ return -1;
+ }
+
+ opt_arg = NULL;
+ } else
+ opt_arg = argv[i];
+
+ }
+
+
+ if (call_function (optn, opt_arg, sym) < 0) {
+ err_bad_res (optn, 0, opt_arg, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXIT)
+ exit (0);
+
+ continue;
+
+
+ handle_arg:
+ if (argument_list) {
+ if (i < MAX_ARGC_NUMBER) /* XXX: ugly, better report */
+ arg_n[num_args++] = i;
+ } else {
+ err_report ("`%s' (argc %d): arguments are not allowed",
+ argv[i], i);
+ return -1;
+ }
+
+ /* POSIX say: No more options after args... */
+ if (posix) option_list = NULL; /* geniously... */
+
+ continue;
+
+
+ handle_short:
+
+ opt_arg = NULL;
+
+ do {
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ if (optn->short_opt && optn->short_opt[0] == *arg)
+ break;
+ }
+ if (!optn->short_opt ||
+ check_sym (optn, sym) < 0
+ ) {
+ err_bad_opt (argv[i], *arg, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXCL) {
+ if (!exclusive_cnt) {
+ err_bad_excl (optn, *arg, i);
+ return -1;
+ }
+ exclusive_cnt--;
+ }
+
+
+ if (optn->arg_name) {
+ unsigned int flags = parse_flags | optn->flags;
+
+ if (arg[1] == '\0') { /* a last one */
+
+ /* POSIX say: an option with arg cannot be grouped. */
+ if (posix && arg != argv[i] && arg[-1] != sym) {
+ err_bad_arg (optn, *arg, i); /* good way? */
+ return -1;
+ }
+
+ if (++i >= argc ||
+ (flags & _CLIF_STRICT_JOIN_ARG)
+ ) {
+ i--;
+
+ if (!(flags & CLIF_OPTARG)) {
+ err_bad_arg (optn, *arg, i);
+ return -1;
+ }
+
+ opt_arg = NULL;
+ } else
+ opt_arg = argv[i];
+ }
+ else if ((arg == argv[i] || arg[-1] == sym) &&
+ (flags & CLIF_MAY_JOIN_ARG)
+ ) {
+ opt_arg = ++arg;
+ }
+ else { /* inside a group... */
+ if (!(flags & CLIF_OPTARG) ||
+ (flags & CLIF_MAY_JOIN_ARG)
+ ) {
+ err_bad_arg (optn, *arg, i);
+ return -1;
+ }
+
+ opt_arg = NULL;
+ }
+ }
+
+ if (call_function (optn, opt_arg, sym) < 0) {
+ err_bad_res (optn, optn->short_opt[0], opt_arg, i);
+ return -1;
+ }
+
+ if (optn->flags & CLIF_EXIT)
+ exit (0);
+
+ } while (!opt_arg && *++arg);
+
+ } /* for ( ... ) */
+
+
+ if ((parse_flags & CLIF_STRICT_EXCL) && exclusive_cnt != 0) {
+ err_report ("One of these must be specified:\n %s\n",
+ show_excl (option_list, 0));
+ return -1;
+ }
+
+
+ /* Now, after *ALL* options, handle arguments, if any. */
+
+ if (num_args < strict_beg + strict_end) {
+ /* Missing some needed arguments. */
+
+ if (num_args < strict_beg) argm = argument_list + num_args;
+ else
+ argm = argument_list +
+ ((num_args - strict_beg) + (num_argm - strict_end));
+
+ if (num_args == strict_beg + strict_end - 1)
+ err_report ("Specify \"%s\" missing argument.", argm->name);
+ else
+ err_report ("Specify \"%s\" and other missing arguments.",
+ argm->name);
+ return -1;
+ }
+
+ if (num_args > 0) {
+ _CLIF_index argm_index[MAX_ARGC_NUMBER];
+
+ /* assing argm (by index) for each arg... */
+
+ for (i = 0, j = 0; i < strict_beg; i++, j++)
+ argm_index[i] = j;
+ for (i = num_args - strict_end, j = num_argm - strict_end;
+ i < num_args; i++, j++
+ ) argm_index[i] = j;
+ for (i = strict_beg, j = strict_beg;
+ i < num_args - strict_end && j < num_argm - strict_end;
+ i++
+ ) {
+ argm_index[i] = j;
+ if (!(argument_list[j].flags & CLIF_MORE))
+ j++;
+ }
+
+ if (i < num_args - strict_end) { /* there are extra args... */
+ err_report ("Extra arg `%s' (position %d, argc %d)",
+ argv[arg_n[i]], i + 1, arg_n[i]);
+ return -1;
+ }
+
+ if (j < num_argm - strict_end &&
+ !(argument_list[j].flags & CLIF_MORE) &&
+ /* ...i.e, there are some missing optional args... */
+ (argument_list[j].flags & CLIF_ACC_PREV)
+ ) {
+ if (j == 0)
+ err_report ("Incorrect argument list set: first arg "
+ "cannot be `accompanied with previous'.");
+ else
+ err_report ("Arg \"%s\" must be specified because "
+ "\"%s\" `%s' is used.", argument_list[j].name,
+ argument_list[j - 1].name, argv[arg_n[i - 1]]);
+ return -1;
+ }
+
+ if (argm_index[--i] == j &&
+ /* above is true only after OPTIONAL area scan
+ and when `j' is stopped on CLIF_MORE */
+ ++j < num_argm - strict_end
+ /* i.e: there is a *last* one (after CLIF_MORE)
+ in the OPTIONAL area */
+ ) argm_index[i] = j; /* *last* is better than *more* */
+
+
+ /* ...and work now */
+
+ for (i = 0; i < num_args; i++) {
+ argm = argument_list + argm_index[i];
+
+ if (argm->function &&
+ argm->function (argm, argv[arg_n[i]], i) < 0
+ ) {
+ err_report ("Cannot handle \"%s\" cmdline arg `%s' "
+ "on position %d (argc %d)",
+ argm->name, argv[arg_n[i]], i + 1, arg_n[i]);
+ return -1;
+ }
+ }
+
+ /* That`s all. */
+ }
+
+ return 0;
+}
+
+
+static void box_output (int start, int left, int width, const char *str,
+ const char *arg_name) {
+ char *p, *endp, *s;
+ int l;
+ char buf[1024];
+ char spacer[128]; /* assume it is enough */
+
+ if (left > sizeof (spacer) - 2) left = sizeof (spacer) - 2;
+ if (width > sizeof (buf) - 1) width = sizeof (buf) - 1;
+
+ spacer[0] = '\n';
+ memset (spacer + 1, ' ', left);
+ spacer[left + 1] = '\0';
+
+
+ l = left - start;
+ if (l > 0) {
+ memset (buf, ' ', l);
+ buf[l] = '\0';
+ fprintf (stderr, "%s", buf);
+ } else
+ fprintf (stderr, "%s", spacer);
+
+
+ endp = buf + width;
+
+ p = buf;
+
+ while (*str) {
+
+ while (*str && p < endp) {
+
+ if (*str == '%' && arg_name) {
+ if (str[1] == '%') {
+ *p++ = '%';
+ str += 2;
+ continue;
+ }
+ else if (str[1] == 's') {
+ const char *a = arg_name;
+
+ while (*a && p < endp) *p++ = *a++;
+ str += 2;
+ continue;
+ }
+ }
+
+ *p++ = *str++;
+ }
+
+ *p = '\0';
+
+ if (p < endp) break;
+
+
+ while (p > buf && *p != ' ' && *p != '\t') p--;
+ if (p <= buf) return; /* foo on you */
+
+ *p = '\0';
+ fprintf (stderr, "%s", buf);
+ fprintf (stderr, "%s", spacer);
+
+ p++;
+ for (s = buf; *p; *s++ = *p++) ;
+ *s = '\0';
+ p = s;
+ }
+
+
+ fprintf (stderr, "%s", buf);
+
+ return;
+}
+
+
+#define SHORT_LONG_DLM " "
+#define OPT_START_DLM " "
+#define OPT_FIELD_WIDTH 30
+
+#define ARG_MARK_STRICT "+ "
+#define ARG_MARK_GROUP0 " . "
+#define ARG_MARK_GROUP " ' "
+#define ARG_MARK_OPT " "
+#define ARG_FIELD_WIDTH 20
+
+#define SCREEN_WIDTH 80
+
+
+void CLIF_print_options (const char *header,
+ const CLIF_option *option_list) {
+ const CLIF_option *optn;
+ char *excl;
+ int excl_cnt = 0;
+
+ /* Print a header string, if present... */
+ if (header) fprintf (stderr, "%s\n", header);
+
+ if (!option_list) return;
+
+
+ for (optn = option_list; optn->short_opt || optn->long_opt; optn++) {
+ int len;
+
+ /* generate and print an option usage */
+
+ if (optn->short_opt) {
+ if (optn->long_opt)
+ len = fprintf (stderr, OPT_START_DLM "%s"
+ SHORT_LONG_DLM "%s",
+ show_short (optn), show_long (optn));
+ else
+ len = fprintf (stderr, OPT_START_DLM "%s",
+ show_short (optn));
+ } else
+ len = fprintf (stderr, OPT_START_DLM "%s", show_long (optn));
+
+
+ /* print a help string, if present */
+
+ if (optn->help_string)
+ box_output (len, OPT_FIELD_WIDTH,
+ SCREEN_WIDTH - OPT_FIELD_WIDTH,
+ optn->help_string, optn->arg_name);
+
+ fprintf (stderr, "\n"); /* a last one */
+ }
+
+ excl = show_excl (option_list, &excl_cnt);
+ if (excl_cnt > 0) {
+
+ if (excl_cnt == 1) {
+ if ((curr.parse_flags & CLIF_STRICT_EXCL) &&
+ curr.option_list == option_list
+ ) fprintf (stderr, "Anyway `%s' must be specified.\n", excl);
+ else /* simple ordinary option, because excl_cnt == 1 ... */;
+ } else
+ fprintf (stderr, "Only one of these may be specified:\n"
+ " %s\n", excl);
+ }
+
+ return;
+}
+
+
+void CLIF_print_arguments (const char *header,
+ const CLIF_argument *argument_list) {
+ const CLIF_argument *argm;
+
+
+ if (!argument_list) return;
+
+ /* Print a header string, if present... */
+ if (header) fprintf (stderr, "%s\n", header);
+
+
+ for (argm = argument_list; argm->name; argm++) {
+ int len;
+
+ if (argm->flags & CLIF_STRICT)
+ len = fprintf (stderr, ARG_MARK_STRICT "%s", argm->name);
+ else if (argm->flags & CLIF_MORE)
+ len = fprintf (stderr, ARG_MARK_OPT "%s ...", argm->name);
+ else if (argm->flags & CLIF_ACC_PREV)
+ len = fprintf (stderr, ARG_MARK_GROUP "%s", argm->name);
+ else if ((argm + 1)->name && ((argm + 1)->flags & CLIF_ACC_PREV))
+ len = fprintf (stderr, ARG_MARK_GROUP0 "%s", argm->name);
+ else
+ len = fprintf (stderr, ARG_MARK_OPT "%s", argm->name);
+
+ if (argm->help_string)
+ box_output (len, ARG_FIELD_WIDTH,
+ SCREEN_WIDTH - ARG_FIELD_WIDTH,
+ argm->help_string, argm->name);
+
+ fprintf (stderr, "\n");
+ }
+
+ return;
+}
+
+
+void CLIF_print_usage (const char *header, const char *progname,
+ const CLIF_option *option_list,
+ const CLIF_argument *argument_list) {
+
+ if (!progname && curr.argv)
+ progname = curr.argv[0];
+
+ if (!header) {
+ if (progname)
+ fprintf (stderr, "Usage: %s", progname);
+ else
+ fprintf (stderr, "Command line options:");
+ } else {
+ if (progname)
+ fprintf (stderr, "%s\n" OPT_START_DLM "%s", header, progname);
+ else
+ fprintf (stderr, "%s", header);
+ }
+
+
+ if (option_list) {
+ const CLIF_option *optn;
+ char m_buf[256], p_buf[256], mp_buf[256];
+ char *m = m_buf, *p = p_buf, *mp = mp_buf;
+ char *end_m = m_buf + sizeof (m_buf) - 1;
+ char *end_p = p_buf + sizeof (p_buf) - 1;
+ char *end_mp = mp_buf + sizeof (mp_buf) - 1;
+ char *excl;
+ int excl_cnt = 0;
+
+
+ /* first, show exclusive option list, if any... */
+
+ excl = show_excl (option_list, &excl_cnt);
+ if (excl_cnt > 0) {
+ if ((curr.parse_flags & CLIF_STRICT_EXCL) &&
+ curr.option_list == option_list
+ ) {
+ if (excl_cnt == 1)
+ fprintf (stderr, " %s", excl);
+ else
+ fprintf (stderr, " { %s }", excl);
+ } else
+ fprintf (stderr, " [ %s ]", excl);
+ }
+
+
+ /* second, find short options without arguments... */
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ /* We don`t exclude CLIF_EXTRA hear:
+ simple one char don`t eat a lot of space...
+ */
+
+ if (!optn->short_opt ||
+ optn->arg_name ||
+ (optn->flags & CLIF_EXCL)
+ ) continue;
+
+ if (optn->function_plus) {
+ if (optn->function) {
+ if (mp < end_mp) *mp++ = optn->short_opt[0];
+ } else {
+ if (p < end_p) *p++ = optn->short_opt[0];
+ }
+ } else {
+ if (m < end_m) *m++ = optn->short_opt[0];
+ }
+ }
+
+ if (m > (char *) m_buf) {
+ *m = '\0';
+ fprintf (stderr, " [ -%s ]", m_buf);
+ }
+ if (p > (char *) p_buf) {
+ *p = '\0';
+ fprintf (stderr, " [ +%s ]", p_buf);
+ }
+ if (mp > (char *) mp_buf) {
+ *mp = '\0';
+ fprintf (stderr, " [ " SHORT_PLUS_MINUS "%s ]", mp_buf);
+ }
+
+
+ /* third, print all another... */
+
+ for (optn = option_list;
+ optn->short_opt || optn->long_opt;
+ optn++
+ ) {
+ if (optn->flags & CLIF_EXTRA) continue;
+
+ if (optn->flags & CLIF_EXCL)
+ continue; /* already handled */
+
+ if (optn->short_opt) {
+ if (optn->arg_name)
+ fprintf (stderr, " [ %s ]", show_short (optn));
+ else
+ /* already handled */;
+ } else
+ fprintf (stderr, " [ %s ]", show_long (optn));
+ }
+ }
+
+
+ if (argument_list) {
+ const CLIF_argument *argm;
+ int deep = 0;
+
+ for (argm = argument_list; argm->name; argm++) {
+
+ if (argm->flags & CLIF_STRICT) {
+ if (deep > 0) {
+ fputc (' ', stderr);
+ while (deep--) fputc (']', stderr);
+ deep = 0;
+ }
+
+ fprintf (stderr, " %s", argm->name);
+ } else {
+ if (argm->flags & CLIF_MORE)
+ fprintf (stderr, " [ %s ...", argm->name);
+ else if (argm->flags & CLIF_ACC_PREV) {
+ fprintf (stderr, " %s", argm->name);
+ --deep; /* ugly, but easy */
+ } else
+ fprintf (stderr, " [ %s", argm->name);
+
+ deep++;
+ }
+ }
+
+ if (deep > 0) {
+ fputc (' ', stderr);
+ while (deep--) fputc (']', stderr);
+ }
+ }
+
+
+ fprintf (stderr, "\n");
+}
+
+
+int CLIF_current_help (void) {
+
+ if (!curr.argc) return -1; /* i.e., not inited... */
+
+ CLIF_print_usage ("Usage:", curr.argv[0], curr.option_list,
+ curr.argument_list);
+
+ if (curr.option_list)
+ CLIF_print_options ("Options:", curr.option_list);
+
+ if (curr.argument_list)
+ CLIF_print_arguments ("\nArguments:", curr.argument_list);
+
+ return 0;
+}
+
+
+/* Common useful option handlers. */
+
+int CLIF_version_handler (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ fprintf (stderr, "%s\n", ((char *) optn->data));
+
+ return 0; /* be happy */
+}
+
+
+int CLIF_set_flag (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ *((int *) optn->data) = 1;
+
+ return 0;
+}
+
+
+int CLIF_unset_flag (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ *((int *) optn->data) = 0;
+
+ return 0;
+}
+
+
+static int set_string (char **data, char *arg) {
+
+ if (!data) return -1;
+
+ *data = arg;
+
+ return 0;
+}
+
+int CLIF_set_string (CLIF_option *optn, char *arg) {
+
+ return set_string (optn->data, arg);
+}
+
+int CLIF_arg_string (CLIF_argument *argm, char *arg, int index) {
+
+ return set_string (argm->data, arg);
+}
+
+
+static int set_int (int *data, char *arg) {
+ char *q;
+
+ if (!data) return -1;
+
+ *data = (int) strtol (arg, &q, 0);
+
+ return (q == arg || *q) ? -1 : 0;
+}
+
+static int set_uint (unsigned int *data, char *arg) {
+ char *q;
+
+ if (!data) return -1;
+
+ *data = (unsigned int) strtoul (arg, &q, 0);
+
+ return (q == arg || *q) ? -1 : 0;
+}
+
+static int set_double (double *data, char *arg) {
+ char *q;
+
+ if (!data) return -1;
+
+ *data = strtod (arg, &q);
+
+ return (q == arg || *q) ? -1 : 0;
+}
+
+
+int CLIF_set_int (CLIF_option *optn, char *arg) {
+
+ return set_int (optn->data, arg);
+}
+
+int CLIF_set_uint (CLIF_option *optn, char *arg) {
+
+ return set_uint (optn->data, arg);
+}
+
+int CLIF_set_double (CLIF_option *optn, char *arg) {
+
+ return set_double (optn->data, arg);
+}
+
+int CLIF_arg_int (CLIF_argument *argm, char *arg, int index) {
+
+ return set_int (argm->data, arg);
+}
+
+int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index) {
+
+ return set_uint (argm->data, arg);
+}
+
+int CLIF_arg_double (CLIF_argument *argm, char *arg, int index) {
+
+ return set_double (argm->data, arg);
+}
+
+
+int CLIF_call_func (CLIF_option *optn, char *arg) {
+
+ if (!optn->data) return -1;
+
+ if (optn->arg_name) {
+ int (*func) (char *) = optn->data;
+
+ return func (arg);
+ } else {
+ int (*func) (void) = optn->data;
+
+ return func ();
+ }
+}
+
+int CLIF_arg_func (CLIF_argument *argm, char *arg, int index) {
+ int (*func) (char *, int);
+
+ if (!argm->data) return -1;
+
+ func = (int (*) (char *, int)) argm->data;
+
+ return func (arg, index);
+}
+
diff --git a/libsupp/clif.h b/libsupp/clif.h
new file mode 100644
index 0000000..6b7f3f0
--- /dev/null
+++ b/libsupp/clif.h
@@ -0,0 +1,121 @@
+/*
+ Copyright (c) 2000, 2003 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: LGPL v2.1 or any later
+
+ See COPYING.LIB for the status of this software.
+*/
+
+#ifndef _CLIF_H
+#define _CLIF_H
+
+
+typedef struct CLIF_option_struct CLIF_option;
+struct CLIF_option_struct {
+ const char *short_opt;
+ const char *long_opt;
+ const char *arg_name;
+ const char *help_string;
+ int (*function) (CLIF_option *optn, char *arg);
+ void *data;
+ int (*function_plus) (CLIF_option *optn, char *arg);
+ unsigned int flags;
+};
+#define CLIF_END_OPTION { 0, 0, 0, 0, 0, 0, 0, 0 }
+
+typedef struct CLIF_argument_struct CLIF_argument;
+struct CLIF_argument_struct {
+ const char *name;
+ const char *help_string;
+ int (*function) (CLIF_argument *argm, char *arg, int index);
+ void *data;
+ unsigned int flags;
+};
+#define CLIF_END_ARGUMENT { 0, 0, 0, 0, 0 }
+
+/* Argument flag bits. */
+#define CLIF_MORE (0x01) /* null or several */
+#define CLIF_STRICT (0x02) /* arg must be present */
+#define CLIF_ACC_PREV (0x04) /* arg must be accompanied with previous */
+
+
+/* Option flag bits. */
+
+/* affected only by per-option flags */
+#define CLIF_EXTRA (0x0001) /* don`t show in usage line */
+#define CLIF_EXIT (0x0002) /* exit after handler return */
+#define CLIF_EXCL (0x0004) /* at exclusive area */
+
+/* affected by per-option flags and by common `parse_flags' argument
+ of CLIF_parse_cmdline(). In last case appropriate bits are translated
+ for all the options.
+*/
+#define CLIF_MAY_JOIN_ARG (0x0010)
+#define _CLIF_STRICT_JOIN_ARG (0x0020)
+#define CLIF_JOIN_ARG (CLIF_MAY_JOIN_ARG|_CLIF_STRICT_JOIN_ARG)
+#define CLIF_MAY_NOEQUAL (0x0040)
+#define _CLIF_STRICT_NOEQUAL (0x0080)
+#define CLIF_NOEQUAL (CLIF_MAY_NOEQUAL|_CLIF_STRICT_NOEQUAL)
+#define CLIF_MAY_KEYWORD (0x0100)
+#define _CLIF_STRICT_KEYWORD (0x0200)
+#define CLIF_KEYWORD (CLIF_MAY_KEYWORD|_CLIF_STRICT_KEYWORD)
+#define CLIF_MAY_ONEDASH (0x0400)
+#define _CLIF_STRICT_ONEDASH (0x0800)
+#define CLIF_ONEDASH (CLIF_MAY_ONEDASH|_CLIF_STRICT_ONEDASH)
+#define CLIF_OPTARG (0x1000) /* allow missing optarg */
+#define CLIF_ABBREV (0x2000) /* allow long opt abbreviation */
+#define CLIF_SEVERAL (0x4000) /* several args in one opt`s arg */
+
+/* affected only by common `parse_flags' arg of CLIF_parse_cmdline() . */
+#define CLIF_HELP_EMPTY (0x10000) /* print help on empty cmdline */
+#define CLIF_POSIX (0x20000) /* follow POSIX standard */
+#define CLIF_FIRST_GROUP (0x40000) /* first arg - options` group */
+#define CLIF_STRICT_EXCL (0x80000) /* at least one exclusive */
+#define CLIF_SILENT (0x100000) /* no errors on stderr */
+
+#define CLIF_MIN_ABBREV 2 /* a minimal match length in abbrev */
+
+
+extern int CLIF_parse (int argc, char **argv, CLIF_option *option_list,
+ CLIF_argument *arg_list, unsigned int parse_flags);
+/* history compatibility... */
+#define CLIF_parse_cmdline(ARGC,ARGV,OPTN,ARGS,FLAGS) \
+ CLIF_parse (ARGC, ARGV, OPTN, ARGS, FLAGS)
+
+extern void CLIF_print_options (const char *header,
+ const CLIF_option *option_list);
+extern void CLIF_print_arguments (const char *header,
+ const CLIF_argument *argument_list);
+extern void CLIF_print_usage (const char *header, const char *progname,
+ const CLIF_option *option_list,
+ const CLIF_argument *argument_list);
+
+extern int CLIF_current_help (void);
+
+/* Common useful option handlers. */
+extern int CLIF_version_handler (CLIF_option *optn, char *arg);
+extern int CLIF_set_flag (CLIF_option *optn, char *arg);
+extern int CLIF_unset_flag (CLIF_option *optn, char *arg);
+extern int CLIF_set_string (CLIF_option *optn, char *arg);
+extern int CLIF_set_int (CLIF_option *optn, char *arg);
+extern int CLIF_set_uint (CLIF_option *optn, char *arg);
+extern int CLIF_set_double (CLIF_option *optn, char *arg);
+extern int CLIF_call_func (CLIF_option *optn, char *arg);
+
+extern int CLIF_arg_string (CLIF_argument *argm, char *arg, int index);
+extern int CLIF_arg_int (CLIF_argument *argm, char *arg, int index);
+extern int CLIF_arg_uint (CLIF_argument *argm, char *arg, int index);
+extern int CLIF_arg_double (CLIF_argument *argm, char *arg, int index);
+extern int CLIF_arg_func (CLIF_argument *argm, char *arg, int index);
+
+
+/* Some useful macros. */
+
+#define CLIF_HELP_OPTION \
+ { 0, "help", 0, "Read this help and exit", \
+ CLIF_call_func, CLIF_current_help, 0, CLIF_EXTRA | CLIF_EXIT }
+#define CLIF_VERSION_OPTION(STR) \
+ { "V", "version", 0, "Print version info and exit", \
+ CLIF_version_handler, STR, 0, CLIF_EXTRA | CLIF_EXIT }
+
+#endif /* _CLIF_H */
diff --git a/store.sh b/store.sh
new file mode 100755
index 0000000..e59f99d
--- /dev/null
+++ b/store.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# Copyright (c) 2000, 2001 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Store package script.
+# Normally invoked by a Makefile.
+#
+# Stores package with an appropriate version info
+# as the main directory postfix (i.e., name-0.1.2/*),
+# even if the source dir don`t have it.
+#
+
+
+[ $# -lt 2 ] && {
+ echo "Usage: $0 target store_dir" >&2
+ exit 2
+}
+
+
+target=$1
+store_dir=$2
+main_dir=`basename \`pwd\``
+
+
+# Find current version info.
+
+dir_v=""
+file_v=""
+
+dir_v=`expr $main_dir : '.*-\([0-9.]*\)$'`
+[ -r VERSION ] && file_v=`awk '/^ *#/ { print $3 ; exit; }
+ /^ *VERSION *=/ {split ($0, a, "="); print a[2]; exit; }
+ { print $0; exit; }' < VERSION `
+[ -n "$file_v" ] && file_v=`echo $file_v ` # to strip possible spaces
+
+[ -z "$file_v" -a -z "$dir_v" ] && {
+ echo "$0: Cannot determine version (use dirname postfix or VERSION file)" >&2
+ exit 2
+}
+
+[ -n "$file_v" -a -n "$dir_v" -a "$dir_v" != "$file_v" ] && {
+ echo "$0: Different version from dirname postfix and VERSION file" >&2
+ exit 2
+}
+
+version="$dir_v"
+[ -z "$version" ] && version="$file_v"
+
+
+targ_vers=${target}-$version
+
+cd ..
+
+[ "$main_dir" != "$targ_vers" ] && {
+ ln -s "$main_dir" "${main_dir}~" || exit 1 # paranoia and paranoia
+ mv -f "$main_dir" "$targ_vers" || exit 1
+}
+
+tar -cvhf - "$targ_vers" | gzip -c -9 > $store_dir/${targ_vers}.tar.gz
+
+[ "$main_dir" != "$targ_vers" ] && {
+ mv -f "$targ_vers" "$main_dir"
+ rm -f "${main_dir}~"
+}
+
+exit 0
diff --git a/traceroute.spec b/traceroute.spec
new file mode 100644
index 0000000..1c95120
--- /dev/null
+++ b/traceroute.spec
@@ -0,0 +1,59 @@
+Summary: Traces the route taken by packets over an IPv4/IPv6 network
+Name: traceroute
+Version: 2.1.5
+Release: 1%{?dist}
+Group: Applications/Internet
+License: GPLv2+
+URL: http://traceroute.sourceforge.net
+Source0: http://dl.sourceforge.net/traceroute/traceroute-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+
+%description
+The traceroute utility displays the route used by IP packets on their
+way to a specified network (or Internet) host. Traceroute displays
+the IP number and host name (if possible) of the machines along the
+route taken by the packets. Traceroute is used as a network debugging
+tool. If you're having network connectivity problems, traceroute will
+show you where the trouble is coming from along the route.
+
+Install traceroute if you need a tool for diagnosing network connectivity
+problems.
+
+
+%prep
+%setup -q
+
+
+%build
+make %{?_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" LDFLAGS=""
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+install -d $RPM_BUILD_ROOT/bin
+install -m755 traceroute/traceroute $RPM_BUILD_ROOT/bin
+pushd $RPM_BUILD_ROOT/bin
+ln -s traceroute traceroute6
+popd
+
+install -d $RPM_BUILD_ROOT%{_mandir}/man8
+install -p -m644 traceroute/traceroute.8 $RPM_BUILD_ROOT%{_mandir}/man8
+ln -s traceroute.8 $RPM_BUILD_ROOT%{_mandir}/man8/traceroute6.8
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING README TODO CREDITS
+/bin/*
+%{_mandir}/*/*
+
+
+%changelog
+* Tue Oct 20 2006 Dmitry Butskoy <Dmitry@Butskoy.name> - 2.0.2-1
+- initial release
diff --git a/traceroute/as_lookups.c b/traceroute/as_lookups.c
new file mode 100644
index 0000000..a9962be
--- /dev/null
+++ b/traceroute/as_lookups.c
@@ -0,0 +1,128 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "traceroute.h"
+
+
+#define DEF_RADB_SERVER "whois.radb.net"
+#define DEF_RADB_SERVICE "nicname"
+
+
+static sockaddr_any ra_addr = {{ 0, }, };
+static char ra_buf[512] = { 0, };
+
+
+const char *get_as_path (const char *query) {
+ int sk, n;
+ FILE *fp;
+ char buf[1024];
+ int prefix = 0, best_prefix = 0;
+ char *rb, *re = &ra_buf[sizeof (ra_buf) / sizeof (*ra_buf) - 1];
+
+
+ if (!ra_addr.sa.sa_family) {
+ const char *server, *service;
+ struct addrinfo *res;
+ int ret;
+
+ server = getenv ("RA_SERVER");
+ if (!server) server = DEF_RADB_SERVER;
+
+ service = getenv ("RA_SERVICE");
+ if (!service) service = DEF_RADB_SERVICE;
+
+
+ ret = getaddrinfo (server, service, NULL, &res);
+ if (ret) {
+ fprintf (stderr, "%s/%s: %s\n", server, service,
+ gai_strerror(ret));
+ exit (2);
+ }
+
+ memcpy (&ra_addr, res->ai_addr, res->ai_addrlen);
+
+ freeaddrinfo (res);
+ }
+
+
+ sk = socket (ra_addr.sa.sa_family, SOCK_STREAM, 0);
+ if (sk < 0) error ("socket");
+
+ if (connect (sk, &ra_addr.sa, sizeof (ra_addr)) < 0)
+ goto err_sk;
+
+ n = snprintf (buf, sizeof (buf), "%s\r\n", query);
+ if (n >= sizeof (buf)) goto err_sk;
+
+ if (write (sk, buf, n) < n)
+ goto err_sk;
+
+ fp = fdopen (sk, "r");
+ if (!fp) goto err_sk;
+
+
+ strcpy (ra_buf, "*");
+ rb = ra_buf;
+
+ while (fgets (buf, sizeof (buf), fp) != NULL) {
+
+ if (!strncmp (buf, "route:", sizeof ("route:") - 1) ||
+ !strncmp (buf, "route6:", sizeof ("route6:") - 1)
+ ) {
+ char *p = strchr (buf, '/');
+
+ if (p) prefix = strtoul (++p, NULL, 10);
+ else prefix = 0; /* Hmmm... */
+
+ }
+ else if (!strncmp (buf, "origin:", sizeof ("origin:") -1)) {
+ char *p, *as;
+
+ p = buf + (sizeof ("origin:") - 1);
+
+ while (isspace (*p)) p++;
+ as = p;
+ while (*p && !isspace (*p)) p++;
+ *p = '\0';
+
+ if (prefix > best_prefix) {
+ best_prefix = prefix;
+
+ rb = ra_buf;
+ while (rb < re && (*rb++ = *as++)) ;
+ }
+ else if (prefix == best_prefix) {
+ char *q = strstr (ra_buf, as);
+
+ if (!q || (*(q += strlen (as)) != '\0' && *q != '/')) {
+ if (rb > ra_buf) rb[-1] = '/';
+ while (rb < re && (*rb++ = *as++)) ;
+ }
+ }
+ /* else just ignore it */
+ }
+ }
+
+ fclose (fp);
+
+ return ra_buf;
+
+
+err_sk:
+ close (sk);
+ return "!!";
+}
diff --git a/traceroute/csum.c b/traceroute/csum.c
new file mode 100644
index 0000000..8b7a920
--- /dev/null
+++ b/traceroute/csum.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "traceroute.h"
+
+
+uint16_t in_csum (const void *ptr, size_t len) {
+ const uint16_t *p = (const uint16_t *) ptr;
+ size_t nw = len / 2;
+ unsigned int sum = 0;
+ uint16_t res;
+
+ while (nw--) sum += *p++;
+
+ if (len & 0x1)
+ sum += htons (*((unsigned char *) p) << 8);
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+
+ res = ~sum;
+ if (!res) res = ~0;
+
+ return res;
+}
+
diff --git a/traceroute/extension.c b/traceroute/extension.c
new file mode 100644
index 0000000..1ad1d48
--- /dev/null
+++ b/traceroute/extension.c
@@ -0,0 +1,237 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "traceroute.h"
+
+
+struct icmp_ext_header {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ unsigned int version:4;
+ unsigned int reserved:4;
+#else
+ unsigned int reserved:4;
+ unsigned int version:4;
+#endif
+ uint8_t reserved1;
+ uint16_t checksum;
+} __attribute__ ((packed));
+
+
+struct icmp_ext_object {
+ uint16_t length;
+ uint8_t class;
+ uint8_t c_type;
+ uint8_t data[0];
+};
+
+#define MPLS_CLASS 1
+#define MPLS_C_TYPE 1
+
+#define IFACE_INFO_CLASS 2
+#define IFACE_INFO_NAME_LEN 64
+
+
+#define do_snprintf(CURR, END, FMT, ARGS...) \
+ do { \
+ CURR += snprintf (CURR, END - CURR, (FMT), ## ARGS);\
+ if (CURR > END) CURR = END; \
+ } while (0)
+
+
+/* rfc 5837 stuff */
+
+static int print_iface_info (struct icmp_ext_object *obj, char *buf, size_t length) {
+ uint32_t *ui;
+ char tmp[128]; /* enough: 4 + (4 + 16) + 64 + 4 = 92 */
+ size_t data_len;
+ char *curr = buf, *end = buf + length;
+ char *start;
+ const char *roles[] = { "INC", "SUB", "OUT", "NXT" };
+
+
+ /* Copy data into temporary array of enough length
+ to avoid boundary checks on each step.
+ */
+ data_len = ntohs (obj->length) - sizeof (*obj);
+ if (data_len > sizeof (tmp)) return 0;
+
+ memset (tmp, 0, sizeof (tmp));
+ memcpy (tmp, obj->data, data_len);
+
+ ui = (uint32_t *) tmp;
+
+
+ do_snprintf (curr, end, "%s:", roles[(obj->c_type >> 6) & 0x03]);
+ start = curr;
+
+ if (obj->c_type & 0x08) /* index */
+ do_snprintf (curr, end, "%u", ntohl (*ui++));
+
+ if (obj->c_type & 0x04) { /* IP address */
+ sockaddr_any addr;
+ void *ptr;
+ size_t len;
+ uint16_t afi = ntohl (*ui++) >> 16;
+
+ memset (&addr, 0, sizeof (addr));
+
+ if (afi == 1) { /* ipv4 */
+ addr.sa.sa_family = AF_INET;
+ ptr = &addr.sin.sin_addr;
+ len = sizeof (addr.sin.sin_addr);
+ }
+ else if (afi == 2) { /* ipv6 */
+ addr.sa.sa_family = AF_INET6;
+ ptr = &addr.sin6.sin6_addr;
+ len = sizeof (addr.sin6.sin6_addr);
+ } else
+ return 0;
+
+ memcpy (ptr, ui, len);
+ ui += len / sizeof (*ui);
+
+ do_snprintf (curr, end, "%s%s", (curr > start) ? "," : "", addr2str (&addr));
+ }
+
+ if (obj->c_type & 0x02) { /* name */
+ uint8_t *name = (uint8_t *) ui;
+ uint8_t len = *name;
+ char str[IFACE_INFO_NAME_LEN * 4]; /* enough... */
+ char *p = str;
+ static char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
+ int i;
+
+ if (!len || (len % sizeof (uint32_t)) || len > IFACE_INFO_NAME_LEN)
+ return 0;
+
+ for (i = 1; i < len; i++) { /* name[0] is length */
+ int ch = name[i];
+
+ if (!ch) break;
+ else if (!isascii (ch) || !isgraph (ch) || ch == '%' || ch == '"') {
+ *p++ = '%';
+ *p++ = hex[(ch >> 4) & 0x0f];
+ *p++ = hex[ch & 0x0f];
+ } else
+ *p++ = ch;
+ }
+ *p++ = '\0';
+
+ do_snprintf (curr, end, "%s\"%s\"", (curr > start) ? "," : "", str);
+
+ ui += len / sizeof (*ui);
+ }
+
+ if (obj->c_type & 0x01) /* mtu */
+ do_snprintf (curr, end, "%smtu=%u", (curr > start) ? "," : "", ntohl (*ui++));
+
+
+ if (ui > (uint32_t *) (tmp + data_len))
+ return 0;
+
+ return (curr - buf);
+}
+
+
+static int try_extension (probe *pb, char *buf, size_t len) {
+ struct icmp_ext_header *iext = (struct icmp_ext_header *) buf;
+ char str[1024];
+ char *curr = str;
+ char *end = str + sizeof (str) / sizeof (*str);
+
+
+ /* a check for len >= 8 already done for all cases */
+
+ if (iext->version != 2) return -1;
+
+ if (iext->checksum &&
+ in_csum (iext, len) != (uint16_t) ~0
+ ) return -1;
+
+ buf += sizeof (*iext);
+ len -= sizeof (*iext);
+
+
+ while (len >= sizeof (struct icmp_ext_object)) {
+ struct icmp_ext_object *obj = (struct icmp_ext_object *) buf;
+ size_t objlen = ntohs (obj->length);
+ size_t data_len;
+ uint32_t *ui = (uint32_t *) obj->data;
+ int i, n;
+
+ if (objlen < sizeof (*obj) ||
+ objlen > len
+ ) return -1;
+
+ data_len = objlen - sizeof (*obj);
+ if (data_len % sizeof (uint32_t))
+ return -1; /* must be 32bit rounded... */
+
+ n = data_len / sizeof (*ui);
+
+
+ if (curr > (char *) str && curr < end)
+ *curr++ = ';'; /* a separator */
+
+ if (obj->class == MPLS_CLASS &&
+ obj->c_type == MPLS_C_TYPE &&
+ n >= 1
+ ) { /* people prefer MPLS (rfc4950) to be parsed... */
+
+ do_snprintf (curr, end, "MPLS:");
+
+ for (i = 0; i < n; i++, ui++) {
+ uint32_t mpls = ntohl (*ui);
+
+ do_snprintf (curr, end, "%sL=%u,E=%u,S=%u,T=%u",
+ i ? "/" : "",
+ mpls >> 12,
+ (mpls >> 9) & 0x7,
+ (mpls >> 8) & 0x1,
+ mpls & 0xff);
+ }
+
+ }
+ else if (obj->class == IFACE_INFO_CLASS &&
+ (i = print_iface_info (obj, curr, end - curr)) > 0
+ ) {
+ curr += i; /* successfully parsed */
+
+ }
+ else { /* common case... */
+
+ do_snprintf (curr, end, "%u/%u:", obj->class, obj->c_type);
+
+ for (i = 0; i < n && curr < end; i++, ui++)
+ do_snprintf (curr, end, "%s%08x", i ? "," : "", ntohl(*ui));
+ }
+
+ buf += objlen;
+ len -= objlen;
+ }
+
+ if (len) return -1;
+
+
+ pb->ext = strdup (str);
+
+ return 0;
+}
+
+
+void handle_extensions (probe *pb, char *buf, int len, int step) {
+
+ if (!step)
+ try_extension (pb, buf, len);
+ else {
+ for ( ; len >= 8; buf += step, len -= step)
+ if (try_extension (pb, buf, len) == 0)
+ break;
+ }
+
+ return;
+}
+
diff --git a/traceroute/flowlabel.h b/traceroute/flowlabel.h
new file mode 100644
index 0000000..af5a65a
--- /dev/null
+++ b/traceroute/flowlabel.h
@@ -0,0 +1,40 @@
+/*
+ It is just a stripped copy of the kernel header "linux/in6.h"
+
+ "Flow label" things are still not defined in "netinet/in*.h" headers,
+ but we cannot use "linux/in6.h" immediately because it currently
+ conflicts with "netinet/in.h" .
+*/
+
+struct in6_flowlabel_req
+{
+ struct in6_addr flr_dst;
+ __u32 flr_label;
+ __u8 flr_action;
+ __u8 flr_share;
+ __u16 flr_flags;
+ __u16 flr_expires;
+ __u16 flr_linger;
+ __u32 __flr_pad;
+ /* Options in format of IPV6_PKTOPTIONS */
+};
+
+#define IPV6_FL_A_GET 0
+#define IPV6_FL_A_PUT 1
+#define IPV6_FL_A_RENEW 2
+
+#define IPV6_FL_F_CREATE 1
+#define IPV6_FL_F_EXCL 2
+
+#define IPV6_FL_S_NONE 0
+#define IPV6_FL_S_EXCL 1
+#define IPV6_FL_S_PROCESS 2
+#define IPV6_FL_S_USER 3
+#define IPV6_FL_S_ANY 255
+
+#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff
+#define IPV6_FLOWINFO_PRIORITY 0x0ff00000
+
+#define IPV6_FLOWLABEL_MGR 32
+#define IPV6_FLOWINFO_SEND 33
+
diff --git a/traceroute/mod-dccp.c b/traceroute/mod-dccp.c
new file mode 100644
index 0000000..5d8518b
--- /dev/null
+++ b/traceroute/mod-dccp.c
@@ -0,0 +1,289 @@
+/*
+ Copyright (c) 2012 Samuel Jero <sj323707@ohio.edu>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <linux/dccp.h>
+
+
+#include "traceroute.h"
+
+
+#define DEF_SERVICE_CODE 1885957735
+
+#define DCCP_HEADER_LEN (sizeof (struct dccp_hdr) + \
+ sizeof (struct dccp_hdr_ext) \
+ + sizeof (struct dccp_hdr_request))
+
+static sockaddr_any dest_addr = {{ 0, }, };
+static unsigned int dest_port = 0;
+
+static int raw_sk = -1;
+static int last_ttl = 0;
+
+static uint8_t buf[1024]; /* enough, enough... */
+static size_t csum_len = 0;
+static struct dccp_hdr *dh = NULL;
+static struct dccp_hdr_ext *dhe = NULL;
+static struct dccp_hdr_request *dhr = NULL;
+static unsigned int service_code = DEF_SERVICE_CODE;
+
+
+static CLIF_option dccp_options[] = {
+ { 0, "service", "NUM", "Set DCCP service code to %s (default is "
+ _TEXT (DEF_SERVICE_CODE) ")",
+ CLIF_set_uint, &service_code, 0, CLIF_ABBREV },
+ CLIF_END_OPTION
+};
+
+
+static int dccp_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+ int af = dest->sa.sa_family;
+ sockaddr_any src;
+ socklen_t len;
+ uint8_t *ptr;
+ uint16_t *lenp;
+
+
+ dest_addr = *dest;
+ dest_addr.sin.sin_port = 0; /* raw sockets can be confused */
+
+ if (!port_seq) port_seq = DEF_DCCP_PORT;
+ dest_port = htons (port_seq);
+
+
+ /* Create raw socket for DCCP */
+ raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP);
+ if (raw_sk < 0)
+ error_or_perm ("socket");
+
+ tune_socket (raw_sk); /* including bind, if any */
+
+ if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0)
+ error ("connect");
+
+ len = sizeof (src);
+ if (getsockname (raw_sk, &src.sa, &len) < 0)
+ error ("getsockname");
+
+
+ if (!raw_can_connect ()) { /* work-around for buggy kernels */
+ close (raw_sk);
+ raw_sk = socket (af, SOCK_RAW, IPPROTO_DCCP);
+ if (raw_sk < 0) error ("socket");
+ tune_socket (raw_sk);
+ /* but do not connect it... */
+ }
+
+
+ use_recverr (raw_sk);
+
+ add_poll (raw_sk, POLLIN | POLLERR);
+
+
+ /* Now create the sample packet. */
+
+ /* For easy checksum computing:
+ saddr
+ daddr
+ length
+ protocol
+ dccphdr
+ */
+
+ ptr = buf;
+
+ if (af == AF_INET) {
+ len = sizeof (src.sin.sin_addr);
+ memcpy (ptr, &src.sin.sin_addr, len);
+ ptr += len;
+ memcpy (ptr, &dest_addr.sin.sin_addr, len);
+ ptr += len;
+ } else {
+ len = sizeof (src.sin6.sin6_addr);
+ memcpy (ptr, &src.sin6.sin6_addr, len);
+ ptr += len;
+ memcpy (ptr, &dest_addr.sin6.sin6_addr, len);
+ ptr += len;
+ }
+
+ lenp = (uint16_t *) ptr;
+ ptr += sizeof (uint16_t);
+ *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_DCCP);
+ ptr += sizeof (uint16_t);
+
+
+ /* Construct DCCP header */
+ dh = (struct dccp_hdr *) ptr;
+
+ dh->dccph_ccval = 0;
+ dh->dccph_checksum = 0;
+ dh->dccph_cscov = 0;
+ dh->dccph_dport = dest_port;
+ dh->dccph_reserved = 0;
+ dh->dccph_sport = 0; /* temporary */
+ dh->dccph_x = 1;
+ dh->dccph_type = DCCP_PKT_REQUEST;
+ dh->dccph_seq2 = 0; /* reserved if using 48 bit sequence numbers */
+ /* high 16 bits of sequence number. Always make 0 for simplicity. */
+ dh->dccph_seq = 0;
+ ptr += sizeof (struct dccp_hdr);
+
+ dhe = (struct dccp_hdr_ext *) ptr;
+ dhe->dccph_seq_low = 0; /* temporary */
+ ptr += sizeof (struct dccp_hdr_ext);
+
+ dhr = (struct dccp_hdr_request *) ptr;
+ dhr->dccph_req_service = htonl (service_code);
+ ptr += sizeof (struct dccp_hdr_request);
+
+
+ csum_len = ptr - buf;
+
+ if (csum_len > sizeof (buf))
+ error ("impossible"); /* paranoia */
+
+ len = ptr - (uint8_t *) dh;
+ if (len & 0x03) error ("impossible"); /* as >>2 ... */
+
+ *lenp = htons (len);
+ dh->dccph_doff = len >> 2;
+
+
+ *packet_len_p = len;
+
+ return 0;
+}
+
+
+static void dccp_send_probe (probe *pb, int ttl) {
+ int sk;
+ int af = dest_addr.sa.sa_family;
+ sockaddr_any addr;
+ socklen_t len = sizeof (addr);
+
+
+ /* To make sure we have chosen a free unused "source port",
+ just create, (auto)bind and hold a socket while the port is needed.
+ */
+
+ sk = socket (af, SOCK_DCCP, IPPROTO_DCCP);
+ if (sk < 0) error ("socket");
+
+ bind_socket (sk);
+
+ if (getsockname (sk, &addr.sa, &len) < 0)
+ error ("getsockname");
+
+ /* When we reach the target host, it can send us either Reset or Response.
+ For Reset all is OK (we and kernel just answer nothing), but
+ for Response we should reply with our Close.
+ It is well-known "half-open technique", used by port scanners etc.
+ This way we do not touch remote applications at all, unlike
+ the ordinary connect(2) call.
+ As the port-holding socket neither connect() nor listen(),
+ it means "no such port yet" for remote ends, and kernel always
+ send Reset in such a situation automatically (we have to do nothing).
+ */
+
+
+ dh->dccph_sport = addr.sin.sin_port;
+
+ dhe->dccph_seq_low = random_seq ();
+
+ dh->dccph_checksum = 0;
+ dh->dccph_checksum = in_csum (buf, csum_len);
+
+
+ if (ttl != last_ttl) {
+ set_ttl (raw_sk, ttl);
+ last_ttl = ttl;
+ }
+
+
+ pb->send_time = get_time ();
+
+ if (do_send (raw_sk, dh, dh->dccph_doff << 2, &dest_addr) < 0) {
+ close (sk);
+ pb->send_time = 0;
+ return;
+ }
+
+
+ pb->seq = dh->dccph_sport;
+
+ pb->sk = sk;
+
+ return;
+}
+
+
+static probe *dccp_check_reply (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len) {
+ probe *pb;
+ struct dccp_hdr *ndh = (struct dccp_hdr *) buf;
+ uint16_t sport, dport;
+
+
+ if (len < 8) return NULL; /* too short */
+
+
+ if (err) {
+ sport = ndh->dccph_sport;
+ dport = ndh->dccph_dport;
+ } else {
+ sport = ndh->dccph_dport;
+ dport = ndh->dccph_sport;
+ }
+
+
+ if (dport != dest_port)
+ return NULL;
+
+ if (!equal_addr (&dest_addr, from))
+ return NULL;
+
+ pb = probe_by_seq (sport);
+ if (!pb) return NULL;
+
+ if (!err) pb->final = 1;
+
+ return pb;
+}
+
+
+static void dccp_recv_probe (int sk, int revents) {
+
+ if (!(revents & (POLLIN | POLLERR)))
+ return;
+
+ recv_reply (sk, !!(revents & POLLERR), dccp_check_reply);
+}
+
+
+static void dccp_expire_probe (probe *pb) {
+
+ probe_done (pb);
+}
+
+
+static tr_module dccp_ops = {
+ .name = "dccp",
+ .init = dccp_init,
+ .send_probe = dccp_send_probe,
+ .recv_probe = dccp_recv_probe,
+ .expire_probe = dccp_expire_probe,
+ .options = dccp_options,
+};
+
+TR_MODULE (dccp_ops);
diff --git a/traceroute/mod-icmp.c b/traceroute/mod-icmp.c
new file mode 100644
index 0000000..d887d3e
--- /dev/null
+++ b/traceroute/mod-icmp.c
@@ -0,0 +1,247 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include "traceroute.h"
+
+
+static sockaddr_any dest_addr = {{ 0, }, };
+static uint16_t seq = 1;
+static uint16_t ident = 0;
+
+static char *data;
+static size_t *length_p;
+
+static int icmp_sk = -1;
+static int last_ttl = 0;
+
+static int raw = 0;
+static int dgram = 0;
+
+
+static CLIF_option icmp_options[] = {
+ { 0, "raw", 0, "Use raw sockets way only. Default is try this way "
+ "first (probably not allowed for unprivileged users), "
+ "then try dgram",
+ CLIF_set_flag, &raw, 0, CLIF_EXCL },
+ { 0, "dgram", 0, "Use dgram sockets way only. May be not implemented "
+ "by old kernels or restricted by sysadmins",
+ CLIF_set_flag, &dgram, 0, CLIF_EXCL },
+ CLIF_END_OPTION
+};
+
+
+static int icmp_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+ int i;
+ int af = dest->sa.sa_family;
+ int protocol;
+
+ dest_addr = *dest;
+ dest_addr.sin.sin_port = 0;
+
+ if (port_seq) seq = port_seq;
+
+ length_p = packet_len_p;
+ if (*length_p < sizeof (struct icmphdr))
+ *length_p = sizeof (struct icmphdr);
+
+ data = malloc (*length_p);
+ if (!data) error ("malloc");
+
+ for (i = sizeof (struct icmphdr); i < *length_p; i++)
+ data[i] = 0x40 + (i & 0x3f);
+
+
+ protocol = (af == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6;
+
+ if (!raw) {
+ icmp_sk = socket (af, SOCK_DGRAM, protocol);
+ if (icmp_sk < 0 && dgram)
+ error ("socket");
+ }
+
+ if (!dgram) {
+ int raw_sk = socket (af, SOCK_RAW, protocol);
+ if (raw_sk < 0) {
+ if (raw || icmp_sk < 0)
+ error_or_perm ("socket");
+ dgram = 1;
+ } else {
+ /* prefer the traditional "raw" way when possible */
+ close (icmp_sk);
+ icmp_sk = raw_sk;
+ }
+ }
+
+
+ tune_socket (icmp_sk);
+
+ /* Don't want to catch packets from another hosts */
+ if (raw_can_connect () &&
+ connect (icmp_sk, &dest_addr.sa, sizeof (dest_addr)) < 0
+ ) error ("connect");
+
+ use_recverr (icmp_sk);
+
+
+ if (dgram) {
+ sockaddr_any addr;
+ socklen_t len = sizeof (addr);
+
+ if (getsockname (icmp_sk, &addr.sa, &len) < 0)
+ error ("getsockname");
+ ident = ntohs (addr.sin.sin_port); /* both IPv4 and IPv6 */
+
+ } else
+ ident = getpid () & 0xffff;
+
+
+ add_poll (icmp_sk, POLLIN | POLLERR);
+
+ return 0;
+}
+
+
+static void icmp_send_probe (probe *pb, int ttl) {
+ int af = dest_addr.sa.sa_family;
+
+
+ if (ttl != last_ttl) {
+
+ set_ttl (icmp_sk, ttl);
+
+ last_ttl = ttl;
+ }
+
+
+ if (af == AF_INET) {
+ struct icmp *icmp = (struct icmp *) data;
+
+ icmp->icmp_type = ICMP_ECHO;
+ icmp->icmp_code = 0;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_id = htons (ident);
+ icmp->icmp_seq = htons (seq);
+
+ icmp->icmp_cksum = in_csum (data, *length_p);
+ }
+ else if (af == AF_INET6) {
+ struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) data;
+
+ icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
+ icmp6->icmp6_code = 0;
+ icmp6->icmp6_cksum = 0;
+ icmp6->icmp6_id = htons (ident);
+ icmp6->icmp6_seq = htons(seq);
+
+ /* icmp6->icmp6_cksum always computed by kernel internally */
+ }
+
+
+ pb->send_time = get_time ();
+
+ if (do_send (icmp_sk, data, *length_p, &dest_addr) < 0) {
+ pb->send_time = 0;
+ return;
+ }
+
+
+ pb->seq = seq;
+
+ seq++;
+
+ return;
+}
+
+
+static probe *icmp_check_reply (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len) {
+ int af = dest_addr.sa.sa_family;
+ int type;
+ uint16_t recv_id, recv_seq;
+ probe *pb;
+
+
+ if (len < sizeof (struct icmphdr))
+ return NULL;
+
+
+ if (af == AF_INET) {
+ struct icmp *icmp = (struct icmp *) buf;
+
+ type = icmp->icmp_type;
+
+ recv_id = ntohs (icmp->icmp_id);
+ recv_seq = ntohs (icmp->icmp_seq);
+
+ }
+ else { /* AF_INET6 */
+ struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf;
+
+ type = icmp6->icmp6_type;
+
+ recv_id = ntohs (icmp6->icmp6_id);
+ recv_seq = ntohs (icmp6->icmp6_seq);
+ }
+
+
+ if (recv_id != ident)
+ return NULL;
+
+ pb = probe_by_seq (recv_seq);
+ if (!pb) return NULL;
+
+
+ if (!err) {
+
+ if (!(af == AF_INET && type == ICMP_ECHOREPLY) &&
+ !(af == AF_INET6 && type == ICMP6_ECHO_REPLY)
+ ) return NULL;
+
+ pb->final = 1;
+ }
+
+ return pb;
+}
+
+
+static void icmp_recv_probe (int sk, int revents) {
+
+ if (!(revents & (POLLIN | POLLERR)))
+ return;
+
+ recv_reply (sk, !!(revents & POLLERR), icmp_check_reply);
+}
+
+
+static void icmp_expire_probe (probe *pb) {
+
+ probe_done (pb);
+}
+
+
+static tr_module icmp_ops = {
+ .name = "icmp",
+ .init = icmp_init,
+ .send_probe = icmp_send_probe,
+ .recv_probe = icmp_recv_probe,
+ .expire_probe = icmp_expire_probe,
+ .options = icmp_options,
+};
+
+TR_MODULE (icmp_ops);
diff --git a/traceroute/mod-raw.c b/traceroute/mod-raw.c
new file mode 100644
index 0000000..8eaac35
--- /dev/null
+++ b/traceroute/mod-raw.c
@@ -0,0 +1,163 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netdb.h>
+
+#include "traceroute.h"
+
+
+static sockaddr_any dest_addr = {{ 0, }, };
+static int protocol = DEF_RAW_PROT;
+
+static char *data = NULL;
+static size_t *length_p;
+
+static int raw_sk = -1;
+static int last_ttl = 0;
+static int seq = 0;
+
+
+static int set_protocol (CLIF_option *optn, char *arg) {
+ char *q;
+
+ protocol = strtoul (arg, &q, 0);
+ if (q == arg) {
+ struct protoent *p = getprotobyname (arg);
+
+ if (!p) return -1;
+ protocol = p->p_proto;
+ }
+
+ return 0;
+}
+
+
+static CLIF_option raw_options[] = {
+ { 0, "protocol", "PROT", "Use protocol %s (default is "
+ _TEXT (DEF_RAW_PROT) ")",
+ set_protocol, 0, 0, CLIF_ABBREV },
+ CLIF_END_OPTION
+};
+
+
+static int raw_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+ int i;
+ int af = dest->sa.sa_family;
+
+ dest_addr = *dest;
+ dest_addr.sin.sin_port = 0;
+
+ if (port_seq) protocol = port_seq;
+
+
+ length_p = packet_len_p;
+
+ if (*length_p &&
+ !(data = malloc (*length_p))
+ ) error ("malloc");
+
+ for (i = 0; i < *length_p; i++)
+ data[i] = 0x40 + (i & 0x3f);
+
+
+ raw_sk = socket (af, SOCK_RAW, protocol);
+ if (raw_sk < 0)
+ error_or_perm ("socket");
+
+ tune_socket (raw_sk);
+
+ /* Don't want to catch packets from another hosts */
+ if (raw_can_connect () &&
+ connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0
+ ) error ("connect");
+
+ use_recverr (raw_sk);
+
+
+ add_poll (raw_sk, POLLIN | POLLERR);
+
+ return 0;
+}
+
+
+static void raw_send_probe (probe *pb, int ttl) {
+
+ if (ttl != last_ttl) {
+
+ set_ttl (raw_sk, ttl);
+
+ last_ttl = ttl;
+ }
+
+
+ pb->send_time = get_time ();
+
+ if (do_send (raw_sk, data, *length_p, &dest_addr) < 0) {
+ pb->send_time = 0;
+ return;
+ }
+
+
+ pb->seq = ++seq;
+
+ return;
+}
+
+
+static probe *raw_check_reply (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len) {
+ probe *pb;
+
+ if (!equal_addr (&dest_addr, from))
+ return NULL;
+
+ pb = probe_by_seq (seq);
+ if (!pb) return NULL;
+
+ if (!err) pb->final = 1;
+
+ return pb;
+}
+
+
+static void raw_recv_probe (int sk, int revents) {
+
+ if (!(revents & (POLLIN | POLLERR)))
+ return;
+
+ recv_reply (sk, !!(revents & POLLERR), raw_check_reply);
+}
+
+
+static void raw_expire_probe (probe *pb) {
+
+ probe_done (pb);
+}
+
+
+static tr_module raw_ops = {
+ .name = "raw",
+ .init = raw_init,
+ .send_probe = raw_send_probe,
+ .recv_probe = raw_recv_probe,
+ .expire_probe = raw_expire_probe,
+ .options = raw_options,
+ .one_per_time = 1,
+};
+
+TR_MODULE (raw_ops);
diff --git a/traceroute/mod-tcp.c b/traceroute/mod-tcp.c
new file mode 100644
index 0000000..c8453bf
--- /dev/null
+++ b/traceroute/mod-tcp.c
@@ -0,0 +1,606 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+
+
+#include "traceroute.h"
+
+
+#ifndef IP_MTU
+#define IP_MTU 14
+#endif
+
+#ifndef TCPOPT_FASTOPEN
+#define TCPOPT_FASTOPEN 34
+#define TCPOLEN_FASTOPEN_BASE 2
+#endif
+
+
+static sockaddr_any dest_addr = {{ 0, }, };
+static unsigned int dest_port = 0;
+
+static int raw_sk = -1;
+static int last_ttl = 0;
+
+static uint8_t buf[1024]; /* enough, enough... */
+static size_t csum_len = 0;
+static struct tcphdr *th = NULL;
+
+#define TH_FLAGS(TH) (((uint8_t *) (TH))[13])
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+#define TH_ECE 0x40
+#define TH_CWR 0x80
+
+
+static int flags = 0; /* & 0xff == tcp_flags ... */
+static int sysctl = 0;
+static int reuse = 0;
+static int mss = -1;
+static int check_mss = 0;
+static int info = 0;
+static int fastopen = 0;
+
+#define FL_FLAGS 0x0100
+#define FL_ECN 0x0200
+#define FL_SACK 0x0400
+#define FL_TSTAMP 0x0800
+#define FL_WSCALE 0x1000
+
+
+static struct {
+ const char *name;
+ unsigned int flag;
+} tcp_flags[] = {
+ { "fin", TH_FIN },
+ { "syn", TH_SYN },
+ { "rst", TH_RST },
+ { "psh", TH_PSH },
+ { "ack", TH_ACK },
+ { "urg", TH_URG },
+ { "ece", TH_ECE },
+ { "cwr", TH_CWR },
+};
+
+
+static char *print_tcp_info (struct tcphdr *tcp, size_t len) {
+ int i;
+ char str[128]; /* enough... */
+ char *curr = str;
+ char *end = str + (sizeof (str) / sizeof (*str) - 1);
+ const char *p;
+ unsigned int flags;
+ uint8_t *ptr;
+
+ if (len < sizeof (struct tcphdr) ||
+ len != tcp->doff << 2
+ ) return NULL;
+
+
+ flags = TH_FLAGS(tcp);
+
+ for (i = 0; i < sizeof (tcp_flags) / sizeof (*tcp_flags); i++) {
+
+ if (!(flags & tcp_flags[i].flag)) continue;
+
+ if (curr > str && curr < end) *curr++ = ',';
+ for (p = tcp_flags[i].name; *p && curr < end; *curr++ = *p++) ;
+ }
+
+
+ ptr = (uint8_t *) (tcp + 1);
+ len -= sizeof (struct tcphdr);
+
+ while (len > 1) {
+ int op = *ptr, oplen = ptr[1];
+ char buf[16];
+ const char *name = NULL;
+
+ switch (op) {
+ case TCPOPT_EOL: len = 0; continue; /* no more... */
+ case TCPOPT_NOP: oplen = 1; break;
+ case TCPOPT_MAXSEG:
+ if (oplen == TCPOLEN_MAXSEG && oplen <= len) {
+ uint16_t rcv_mss = ntohs (*((uint16_t *) (ptr + 2)));
+ snprintf (buf, sizeof (buf), "mss=%u", rcv_mss);
+ name = buf;
+ }
+ break;
+ case TCPOPT_SACK_PERMITTED: if (oplen == TCPOLEN_SACK_PERMITTED) name = "sack"; break;
+ case TCPOPT_TIMESTAMP: if (oplen == TCPOLEN_TIMESTAMP) name = "timestamps"; break;
+ case TCPOPT_WINDOW: if (oplen == TCPOLEN_WINDOW) name = "window_scaling"; break;
+ case TCPOPT_FASTOPEN: if (oplen >= TCPOLEN_FASTOPEN_BASE) name = "fastopen"; break;
+ }
+
+ if (name) {
+ if (curr > str && curr < end) *curr++ = ',';
+ for (p = name; *p && curr < end; *curr++ = *p++) ;
+ }
+
+ if (len < oplen) break;
+ len -= oplen;
+ ptr += oplen;
+ }
+
+
+ *curr = '\0';
+
+ return strdup (str);
+}
+
+
+static int set_tcp_flag (CLIF_option *optn, char *arg) {
+ int i;
+
+ for (i = 0; i < sizeof (tcp_flags) / sizeof (*tcp_flags); i++) {
+ if (!strcmp (optn->long_opt, tcp_flags[i].name)) {
+ flags |= tcp_flags[i].flag;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int set_tcp_flags (CLIF_option *optn, char *arg) {
+ char *q;
+ unsigned long value;
+
+ value = strtoul (arg, &q, 0);
+ if (q == arg) return -1;
+
+ flags = (flags & ~0xff) | (value & 0xff) | FL_FLAGS;
+ return 0;
+}
+
+static int set_flag (CLIF_option *optn, char *arg) {
+
+ flags |= (unsigned long) optn->data;
+
+ return 0;
+}
+
+static int set_mss (CLIF_option *optn, char *arg) {
+
+ check_mss = 1;
+
+ if (arg)
+ return CLIF_set_uint (optn, arg);
+
+ return 0;
+}
+
+static CLIF_option tcp_options[] = {
+ { 0, "syn", 0, "Set tcp flag SYN (default if no other "
+ "tcp flags specified)", set_tcp_flag, 0, 0, 0 },
+ { 0, "ack", 0, "Set tcp flag ACK,", set_tcp_flag, 0, 0, 0 },
+ { 0, "fin", 0, "FIN,", set_tcp_flag, 0, 0, 0 },
+ { 0, "rst", 0, "RST,", set_tcp_flag, 0, 0, 0 },
+ { 0, "psh", 0, "PSH,", set_tcp_flag, 0, 0, 0 },
+ { 0, "urg", 0, "URG,", set_tcp_flag, 0, 0, 0 },
+ { 0, "ece", 0, "ECE,", set_tcp_flag, 0, 0, 0 },
+ { 0, "cwr", 0, "CWR", set_tcp_flag, 0, 0, 0 },
+ { 0, "flags", "NUM", "Set tcp flags exactly to value %s",
+ set_tcp_flags, 0, 0, CLIF_ABBREV },
+ { 0, "ecn", 0, "Send syn packet with tcp flags ECE and CWR "
+ "(for Explicit Congestion Notification, rfc3168)",
+ set_flag, (void *) FL_ECN, 0, 0 },
+ { 0, "sack", 0, "Use sack,",
+ set_flag, (void *) FL_SACK, 0, 0 },
+ { 0, "timestamps", 0, "timestamps,",
+ set_flag, (void *) FL_TSTAMP, 0, CLIF_ABBREV },
+ { 0, "window_scaling", 0, "window_scaling option for tcp",
+ set_flag, (void *) FL_WSCALE, 0, CLIF_ABBREV },
+ { 0, "sysctl", 0, "Use current sysctl (/proc/sys/net/*) setting "
+ "for the tcp options above and ecn. Always set by default "
+ "(with \"syn\") if nothing else specified",
+ CLIF_set_flag, &sysctl, 0, 0 },
+ { 0, "fastopen", 0, "Use fastopen tcp option (when syn, cookie negotiation only)",
+ CLIF_set_flag, &fastopen, 0, 0 },
+ { 0, "reuse", 0, "Allow to reuse local port numbers "
+ "for the huge workloads (SO_REUSEADDR)",
+ CLIF_set_flag, &reuse, 0, 0 },
+ { 0, "mss", "NUM", "Use value of %s (or unchanged) for maxseg tcp option (when syn), "
+ "and discover its clamping along the path being traced",
+ set_mss, &mss, 0, CLIF_OPTARG },
+ { 0, "info", 0, "Print tcp flags and options of final tcp replies "
+ "when target host is reached. Useful to determine whether "
+ "an application listens the port etc.",
+ CLIF_set_flag, &info, 0, 0 },
+ CLIF_END_OPTION
+};
+
+
+#define SYSCTL_PREFIX "/proc/sys/net/ipv4/tcp_"
+static int check_sysctl (const char *name) {
+ int fd, res;
+ char buf[sizeof (SYSCTL_PREFIX) + strlen (name) + 1];
+ uint8_t ch;
+
+ strcpy (buf, SYSCTL_PREFIX);
+ strcat (buf, name);
+
+ fd = open (buf, O_RDONLY, 0);
+ if (fd < 0) return 0;
+
+ res = read (fd, &ch, sizeof (ch));
+ close (fd);
+
+ if (res != sizeof (ch))
+ return 0;
+
+ /* since kernel 2.6.31 "tcp_ecn" can have value of '2'... */
+ if (ch == '1') return 1;
+
+ return 0;
+}
+
+
+static int tcp_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+ int af = dest->sa.sa_family;
+ sockaddr_any src;
+ int mtu;
+ socklen_t len;
+ uint8_t *ptr;
+ uint16_t *lenp;
+
+
+ dest_addr = *dest;
+ dest_addr.sin.sin_port = 0; /* raw sockets can be confused */
+
+ if (!port_seq) port_seq = DEF_TCP_PORT;
+ dest_port = htons (port_seq);
+
+
+ /* Create raw socket for tcp */
+
+ raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP);
+ if (raw_sk < 0)
+ error_or_perm ("socket");
+
+ tune_socket (raw_sk); /* including bind, if any */
+
+ if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0)
+ error ("connect");
+
+ len = sizeof (src);
+ if (getsockname (raw_sk, &src.sa, &len) < 0)
+ error ("getsockname");
+
+
+ len = sizeof (mtu);
+ if (getsockopt (raw_sk, af == AF_INET ? SOL_IP : SOL_IPV6,
+ af == AF_INET ? IP_MTU : IPV6_MTU,
+ &mtu, &len) < 0 || mtu < 576
+ ) mtu = 576;
+
+ /* mss = mtu - headers */
+ mtu -= af == AF_INET ? sizeof (struct iphdr) : sizeof (struct ip6_hdr);
+ mtu -= sizeof (struct tcphdr);
+
+ if (mss < 0) mss = mtu;
+
+
+ if (!raw_can_connect ()) { /* work-around for buggy kernels */
+ close (raw_sk);
+ raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP);
+ if (raw_sk < 0) error ("socket");
+ tune_socket (raw_sk);
+ /* but do not connect it... */
+ }
+
+
+ use_recverr (raw_sk);
+
+ add_poll (raw_sk, POLLIN | POLLERR);
+
+
+ /* Now create the sample packet. */
+
+ if (!flags) sysctl = 1;
+
+ if (sysctl) {
+ if (check_sysctl ("ecn")) flags |= FL_ECN;
+ if (check_sysctl ("sack")) flags |= FL_SACK;
+ if (check_sysctl ("timestamps")) flags |= FL_TSTAMP;
+ if (check_sysctl ("window_scaling")) flags |= FL_WSCALE;
+ }
+
+ if (!(flags & (FL_FLAGS | 0xff))) { /* no any tcp flag set */
+ flags |= TH_SYN;
+ if (flags & FL_ECN)
+ flags |= TH_ECE | TH_CWR;
+ }
+
+
+ /* For easy checksum computing:
+ saddr
+ daddr
+ length
+ protocol
+ tcphdr
+ tcpoptions
+ */
+
+ ptr = buf;
+
+ if (af == AF_INET) {
+ len = sizeof (src.sin.sin_addr);
+ memcpy (ptr, &src.sin.sin_addr, len);
+ ptr += len;
+ memcpy (ptr, &dest_addr.sin.sin_addr, len);
+ ptr += len;
+ } else {
+ len = sizeof (src.sin6.sin6_addr);
+ memcpy (ptr, &src.sin6.sin6_addr, len);
+ ptr += len;
+ memcpy (ptr, &dest_addr.sin6.sin6_addr, len);
+ ptr += len;
+ }
+
+ lenp = (uint16_t *) ptr;
+ ptr += sizeof (uint16_t);
+ *((uint16_t *) ptr) = htons ((uint16_t) IPPROTO_TCP);
+ ptr += sizeof (uint16_t);
+
+
+ /* Construct TCP header */
+
+ th = (struct tcphdr *) ptr;
+
+ th->source = 0; /* temporary */
+ th->dest = dest_port;
+ th->seq = 0; /* temporary */
+ th->ack_seq = 0;
+ th->doff = 0; /* later... */
+ TH_FLAGS(th) = flags & 0xff;
+ th->window = htons (4 * mtu);
+ th->check = 0;
+ th->urg_ptr = 0;
+
+
+ /* Build TCP options */
+
+ ptr = (uint8_t *) (th + 1);
+
+ if (flags & TH_SYN) {
+ *ptr++ = TCPOPT_MAXSEG; /* 2 */
+ *ptr++ = TCPOLEN_MAXSEG; /* 4 */
+ *((uint16_t *) ptr) = htons (mss);
+ ptr += sizeof (uint16_t);
+ }
+
+ if (flags & FL_TSTAMP) {
+
+ if (flags & FL_SACK) {
+ *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */
+ *ptr++ = TCPOLEN_SACK_PERMITTED;/* 2 */
+ } else {
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ }
+ *ptr++ = TCPOPT_TIMESTAMP; /* 8 */
+ *ptr++ = TCPOLEN_TIMESTAMP; /* 10 */
+
+ *((uint32_t *) ptr) = random_seq (); /* really! */
+ ptr += sizeof (uint32_t);
+ *((uint32_t *) ptr) = (flags & TH_ACK) ? random_seq () : 0;
+ ptr += sizeof (uint32_t);
+ }
+ else if (flags & FL_SACK) {
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */
+ *ptr++ = TCPOLEN_SACK_PERMITTED; /* 2 */
+ }
+
+ if (flags & FL_WSCALE) {
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ *ptr++ = TCPOPT_WINDOW; /* 3 */
+ *ptr++ = TCPOLEN_WINDOW; /* 3 */
+ *ptr++ = 2; /* assume some corect value... */
+ }
+
+ if (fastopen && (flags & TH_SYN)) {
+ *ptr++ = TCPOPT_FASTOPEN; /* 34 */
+ if (flags & TH_ACK) {
+ /* cookie size of 8 is defined in kernel's linux/tcp.h */
+ *ptr++ = TCPOLEN_FASTOPEN_BASE + 2 * sizeof (uint32_t);
+ *((uint32_t *) ptr) = random_seq (); ptr += sizeof (uint32_t);
+ *((uint32_t *) ptr) = random_seq (); ptr += sizeof (uint32_t);
+ } else
+ *ptr++ = TCPOLEN_FASTOPEN_BASE + 0; /* 2 */
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ *ptr++ = TCPOPT_NOP; /* 1 */
+ }
+
+
+ csum_len = ptr - buf;
+
+ if (csum_len > sizeof (buf))
+ error ("impossible"); /* paranoia */
+
+ len = ptr - (uint8_t *) th;
+ if (len & 0x03) error ("impossible"); /* as >>2 ... */
+
+ *lenp = htons (len);
+ th->doff = len >> 2;
+
+
+ *packet_len_p = len;
+
+ return 0;
+}
+
+
+static void tcp_send_probe (probe *pb, int ttl) {
+ int sk;
+ int af = dest_addr.sa.sa_family;
+ sockaddr_any addr;
+ socklen_t len = sizeof (addr);
+
+
+ /* To make sure we have chosen a free unused "source port",
+ just create, (auto)bind and hold a socket while the port is needed.
+ */
+
+ sk = socket (af, SOCK_STREAM, 0);
+ if (sk < 0) error ("socket");
+
+ if (reuse && setsockopt (sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
+ error ("setsockopt SO_REUSEADDR");
+
+ bind_socket (sk);
+
+ if (getsockname (sk, &addr.sa, &len) < 0)
+ error ("getsockname");
+
+ /* When we reach the target host, it can send us either RST or SYN+ACK.
+ For RST all is OK (we and kernel just answer nothing), but
+ for SYN+ACK we should reply with our RST.
+ It is well-known "half-open technique", used by port scanners etc.
+ This way we do not touch remote applications at all, unlike
+ the ordinary connect(2) call.
+ As the port-holding socket neither connect() nor listen(),
+ it means "no such port yet" for remote ends, and kernel always
+ send RST in such a situation automatically (we have to do nothing).
+ */
+
+
+ th->source = addr.sin.sin_port;
+
+ th->seq = random_seq ();
+
+ th->check = 0;
+ th->check = in_csum (buf, csum_len);
+
+
+ if (ttl != last_ttl) {
+
+ set_ttl (raw_sk, ttl);
+
+ last_ttl = ttl;
+ }
+
+
+ pb->send_time = get_time ();
+
+ if (do_send (raw_sk, th, th->doff << 2, &dest_addr) < 0) {
+ close (sk);
+ pb->send_time = 0;
+ return;
+ }
+
+
+ pb->seq = th->source;
+
+ pb->sk = sk;
+
+ return;
+}
+
+
+static probe *tcp_check_reply (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len) {
+ probe *pb;
+ struct tcphdr *tcp = (struct tcphdr *) buf;
+ uint16_t sport, dport;
+
+
+ if (len < 8) return NULL; /* too short */
+
+
+ if (err) {
+ sport = tcp->source;
+ dport = tcp->dest;
+ } else {
+ sport = tcp->dest;
+ dport = tcp->source;
+ }
+
+
+ if (dport != dest_port)
+ return NULL;
+
+ if (!equal_addr (&dest_addr, from))
+ return NULL;
+
+ pb = probe_by_seq (sport);
+ if (!pb) return NULL;
+
+
+ if (check_mss &&
+ err &&
+ len >= sizeof (*tcp) + TCPOLEN_MAXSEG
+ ) {
+ uint8_t *ptr = (uint8_t *) (tcp + 1);
+
+ if (ptr[0] == TCPOPT_MAXSEG && ptr[1] == TCPOLEN_MAXSEG) {
+ uint16_t seen_mss = ntohs (*((uint16_t *) (ptr + 2)));
+ if (mss != seen_mss) {
+ put_err (pb, "M=%u", seen_mss);
+ mss = seen_mss; /* print just once */
+ }
+ }
+ }
+
+
+ if (!err) {
+
+ pb->final = 1;
+
+ if (info)
+ pb->ext = print_tcp_info (tcp, len);
+ }
+
+ return pb;
+}
+
+
+static void tcp_recv_probe (int sk, int revents) {
+
+ if (!(revents & (POLLIN | POLLERR)))
+ return;
+
+ recv_reply (sk, !!(revents & POLLERR), tcp_check_reply);
+}
+
+
+static void tcp_expire_probe (probe *pb) {
+
+ probe_done (pb);
+}
+
+
+static tr_module tcp_ops = {
+ .name = "tcp",
+ .init = tcp_init,
+ .send_probe = tcp_send_probe,
+ .recv_probe = tcp_recv_probe,
+ .expire_probe = tcp_expire_probe,
+ .options = tcp_options,
+};
+
+TR_MODULE (tcp_ops);
diff --git a/traceroute/mod-tcpconn.c b/traceroute/mod-tcpconn.c
new file mode 100644
index 0000000..56213b9
--- /dev/null
+++ b/traceroute/mod-tcpconn.c
@@ -0,0 +1,228 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <errno.h>
+
+#include "traceroute.h"
+
+
+static sockaddr_any dest_addr = {{ 0, }, };
+
+static int icmp_sk = -1;
+
+
+static int tcp_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+ int af = dest->sa.sa_family;
+
+ dest_addr = *dest;
+ dest_addr.sin.sin_port = htons (DEF_TCP_PORT);
+
+ if (port_seq)
+ dest_addr.sin.sin_port = htons (port_seq);
+
+
+ /* Currently an ICMP socket is the only way
+ to obtain the needed info...
+ */
+ icmp_sk = socket (af, SOCK_RAW, (af == AF_INET) ? IPPROTO_ICMP
+ : IPPROTO_ICMPV6);
+ if (icmp_sk < 0)
+ error_or_perm ("socket");
+
+ /* icmp_sk not need full tune_socket() here, just a receiving one */
+ bind_socket (icmp_sk);
+ use_timestamp (icmp_sk);
+ use_recv_ttl (icmp_sk);
+
+ add_poll (icmp_sk, POLLIN);
+
+ return 0;
+}
+
+
+static void tcp_send_probe (probe *pb, int ttl) {
+ int sk;
+ int af = dest_addr.sa.sa_family;
+ sockaddr_any addr;
+ socklen_t length = sizeof (addr);
+
+
+ sk = socket (af, SOCK_STREAM, 0);
+ if (sk < 0) error ("socket");
+
+ tune_socket (sk); /* common stuff */
+
+ set_ttl (sk, ttl);
+
+
+ pb->send_time = get_time ();
+
+ if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) {
+ if (errno != EINPROGRESS)
+ error ("connect");
+ }
+
+
+ if (getsockname (sk, &addr.sa, &length) < 0)
+ error ("getsockname");
+
+ pb->seq = addr.sin.sin_port; /* both ipv4/ipv6 */
+
+ pb->sk = sk;
+
+ add_poll (sk, POLLERR | POLLHUP | POLLOUT);
+
+ return;
+}
+
+
+static probe *tcp_check_reply (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len) {
+ int af = dest_addr.sa.sa_family;
+ int type, code, info;
+ probe *pb;
+ struct tcphdr *tcp;
+
+
+ if (len < sizeof (struct icmphdr))
+ return NULL;
+
+
+ if (af == AF_INET) {
+ struct icmp *icmp = (struct icmp *) buf;
+ struct iphdr *ip;
+ int hlen;
+
+ type = icmp->icmp_type;
+ code = icmp->icmp_code;
+ info = icmp->icmp_void;
+
+ if (type != ICMP_TIME_EXCEEDED && type != ICMP_DEST_UNREACH)
+ return NULL;
+
+ if (len < sizeof (struct icmphdr) + sizeof (struct iphdr) + 8)
+ /* `8' - rfc1122: 3.2.2 */
+ return NULL;
+
+ ip = (struct iphdr *) (((char *)icmp) + sizeof(struct icmphdr));
+ hlen = ip->ihl << 2;
+
+ if (len < sizeof (struct icmphdr) + hlen + 8)
+ return NULL;
+ if (ip->protocol != IPPROTO_TCP)
+ return NULL;
+
+ tcp = (struct tcphdr *) (((char *) ip) + hlen);
+
+ }
+ else { /* AF_INET6 */
+ struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) buf;
+ struct ip6_hdr *ip6;
+
+ type = icmp6->icmp6_type;
+ code = icmp6->icmp6_code;
+ info = icmp6->icmp6_mtu;
+
+ if (type != ICMP6_TIME_EXCEEDED &&
+ type != ICMP6_DST_UNREACH &&
+ type != ICMP6_PACKET_TOO_BIG
+ ) return NULL;
+
+ if (len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + 8)
+ return NULL;
+
+ ip6 = (struct ip6_hdr *) (icmp6 + 1);
+ if (ip6->ip6_nxt != IPPROTO_TCP)
+ return NULL;
+
+ tcp = (struct tcphdr *) (ip6 + 1);
+
+ }
+
+
+ if (tcp->dest != dest_addr.sin.sin_port)
+ return NULL;
+
+ pb = probe_by_seq (tcp->source);
+ if (!pb) return NULL;
+
+
+ /* here only, high level has no data to do this */
+ parse_icmp_res (pb, type, code, info);
+
+ return pb;
+}
+
+
+static void tcp_recv_probe (int sk, int revents) {
+
+ if (sk != icmp_sk) { /* a tcp socket */
+ probe *pb;
+
+ pb = probe_by_sk (sk);
+ if (!pb) {
+ del_poll (sk);
+ return;
+ }
+
+
+ /* do connect() again and check errno, regardless of revents */
+ if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0) {
+ if (errno != EISCONN && errno != ECONNREFUSED)
+ return; /* ICMP say more */
+ }
+
+ /* we have reached the dest host (either connected or refused) */
+
+ memcpy (&pb->res, &dest_addr, sizeof (pb->res));
+
+ pb->final = 1;
+
+ pb->recv_time = get_time ();
+
+ probe_done (pb);
+
+ return;
+ }
+
+
+ /* ICMP stuff */
+
+ if (!(revents & POLLIN))
+ return;
+
+ recv_reply (icmp_sk, 0, tcp_check_reply);
+}
+
+
+static void tcp_expire_probe (probe *pb) {
+
+ probe_done (pb);
+}
+
+
+static tr_module tcp_ops = {
+ .name = "tcpconn",
+ .init = tcp_init,
+ .send_probe = tcp_send_probe,
+ .recv_probe = tcp_recv_probe,
+ .expire_probe = tcp_expire_probe,
+};
+
+TR_MODULE (tcp_ops);
diff --git a/traceroute/mod-udp.c b/traceroute/mod-udp.c
new file mode 100644
index 0000000..45f3a6e
--- /dev/null
+++ b/traceroute/mod-udp.c
@@ -0,0 +1,234 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+
+#include "traceroute.h"
+
+
+#ifndef IPPROTO_UDPLITE
+#define IPPROTO_UDPLITE 136
+#endif
+
+#ifndef UDPLITE_SEND_CSCOV
+#define UDPLITE_SEND_CSCOV 10
+#define UDPLITE_RECV_CSCOV 11
+#endif
+
+
+static sockaddr_any dest_addr = {{ 0, }, };
+static unsigned int curr_port = 0;
+static unsigned int protocol = IPPROTO_UDP;
+
+
+static char *data = NULL;
+static size_t *length_p;
+
+static void fill_data (size_t *packet_len_p) {
+ int i;
+
+ length_p = packet_len_p;
+
+ if (*length_p &&
+ !(data = malloc (*length_p))
+ ) error ("malloc");
+
+ for (i = 0; i < *length_p; i++)
+ data[i] = 0x40 + (i & 0x3f);
+
+ return;
+}
+
+
+static int udp_default_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+
+ curr_port = port_seq ? port_seq : DEF_START_PORT;
+
+ dest_addr = *dest;
+ dest_addr.sin.sin_port = htons (curr_port);
+
+ fill_data (packet_len_p);
+
+ return 0;
+}
+
+
+static int udp_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+
+ dest_addr = *dest;
+
+ if (!port_seq) port_seq = DEF_UDP_PORT;
+ dest_addr.sin.sin_port = htons ((uint16_t) port_seq);
+
+ fill_data (packet_len_p);
+
+ return 0;
+}
+
+
+static unsigned int coverage = 0;
+#define MIN_COVERAGE (sizeof (struct udphdr))
+
+static void set_coverage (int sk) {
+ int val = MIN_COVERAGE;
+
+ if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV,
+ &coverage, sizeof (coverage)) < 0
+ ) error ("UDPLITE_SEND_CSCOV");
+
+ if (setsockopt (sk, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV,
+ &val, sizeof (val)) < 0
+ ) error ("UDPLITE_RECV_CSCOV");
+}
+
+static CLIF_option udplite_options[] = {
+ { 0, "coverage", "NUM", "Set udplite send coverage to %s (default is "
+ _TEXT(MIN_COVERAGE) ")",
+ CLIF_set_uint, &coverage, 0, CLIF_ABBREV },
+ CLIF_END_OPTION
+};
+
+static int udplite_init (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len_p) {
+
+ dest_addr = *dest;
+
+ if (!port_seq) port_seq = DEF_UDP_PORT; /* XXX: Hmmm... */
+ dest_addr.sin.sin_port = htons ((uint16_t) port_seq);
+
+ protocol = IPPROTO_UDPLITE;
+
+ if (!coverage) coverage = MIN_COVERAGE;
+
+ fill_data (packet_len_p);
+
+ return 0;
+}
+
+
+static void udp_send_probe (probe *pb, int ttl) {
+ int sk;
+ int af = dest_addr.sa.sa_family;
+
+
+ sk = socket (af, SOCK_DGRAM, protocol);
+ if (sk < 0) error ("socket");
+
+ tune_socket (sk); /* common stuff */
+
+ if (coverage) set_coverage (sk); /* udplite case */
+
+ set_ttl (sk, ttl);
+
+
+ if (connect (sk, &dest_addr.sa, sizeof (dest_addr)) < 0)
+ error ("connect");
+
+ use_recverr (sk);
+
+
+ pb->send_time = get_time ();
+
+ if (do_send (sk, data, *length_p, NULL) < 0) {
+ close (sk);
+ pb->send_time = 0;
+ return;
+ }
+
+
+ pb->sk = sk;
+
+ add_poll (sk, POLLIN | POLLERR);
+
+ pb->seq = dest_addr.sin.sin_port;
+
+ if (curr_port) { /* traditional udp method */
+ curr_port++;
+ dest_addr.sin.sin_port = htons (curr_port); /* both ipv4 and ipv6 */
+ }
+
+ return;
+}
+
+
+static probe *udp_check_reply (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len) {
+ probe *pb;
+
+ pb = probe_by_sk (sk);
+ if (!pb) return NULL;
+
+ if (pb->seq != from->sin.sin_port)
+ return NULL;
+
+ if (!err) pb->final = 1;
+
+ return pb;
+}
+
+
+static void udp_recv_probe (int sk, int revents) {
+
+ if (!(revents & (POLLIN | POLLERR)))
+ return;
+
+ recv_reply (sk, !!(revents & POLLERR), udp_check_reply);
+}
+
+
+static void udp_expire_probe (probe *pb) {
+
+ probe_done (pb);
+}
+
+
+/* All three modules share the same methods except the init... */
+
+static tr_module default_ops = {
+ .name = "default",
+ .init = udp_default_init,
+ .send_probe = udp_send_probe,
+ .recv_probe = udp_recv_probe,
+ .expire_probe = udp_expire_probe,
+ .header_len = sizeof (struct udphdr),
+};
+
+TR_MODULE (default_ops);
+
+
+static tr_module udp_ops = {
+ .name = "udp",
+ .init = udp_init,
+ .send_probe = udp_send_probe,
+ .recv_probe = udp_recv_probe,
+ .expire_probe = udp_expire_probe,
+ .header_len = sizeof (struct udphdr),
+};
+
+TR_MODULE (udp_ops);
+
+
+static tr_module udplite_ops = {
+ .name = "udplite",
+ .init = udplite_init,
+ .send_probe = udp_send_probe,
+ .recv_probe = udp_recv_probe,
+ .expire_probe = udp_expire_probe,
+ .header_len = sizeof (struct udphdr),
+ .options = udplite_options,
+};
+
+TR_MODULE (udplite_ops);
diff --git a/traceroute/module.c b/traceroute/module.c
new file mode 100644
index 0000000..cdaade2
--- /dev/null
+++ b/traceroute/module.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "traceroute.h"
+
+
+static tr_module *base = NULL;
+
+void tr_register_module (tr_module *ops) {
+
+ ops->next = base;
+ base = ops;
+}
+
+const tr_module *tr_get_module (const char *name) {
+ const tr_module *ops;
+
+ if (!name) return 0;
+
+ for (ops = base; ops; ops = ops->next) {
+ if (!strcasecmp (name, ops->name))
+ return ops;
+ }
+
+ return NULL;
+}
diff --git a/traceroute/poll.c b/traceroute/poll.c
new file mode 100644
index 0000000..192cdfe
--- /dev/null
+++ b/traceroute/poll.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <poll.h>
+#include <errno.h>
+#include <math.h>
+
+#include "traceroute.h"
+
+
+static struct pollfd *pfd = NULL;
+static unsigned int num_polls = 0;
+
+
+void add_poll (int fd, int events) {
+ int i;
+
+ for (i = 0; i < num_polls && pfd[i].fd > 0; i++) ;
+
+ if (i == num_polls) {
+ pfd = realloc (pfd, ++num_polls * sizeof (*pfd));
+ if (!pfd) error ("realloc");
+ }
+
+ pfd[i].fd = fd;
+ pfd[i].events = events;
+}
+
+
+void del_poll (int fd) {
+ int i;
+
+ for (i = 0; i < num_polls && pfd[i].fd != fd; i++) ;
+
+ if (i < num_polls) pfd[i].fd = -1; /* or just zero it... */
+}
+
+
+static int cleanup_polls (void) {
+ int i;
+
+ for (i = 0; i < num_polls && pfd[i].fd > 0; i++) ;
+
+ if (i < num_polls) { /* a hole have found */
+ int j;
+
+ for (j = i + 1; j < num_polls; j++) {
+ if (pfd[j].fd > 0) {
+ pfd[i++] = pfd[j];
+ pfd[j].fd = -1;
+ }
+ }
+ }
+
+ return i;
+}
+
+
+void do_poll (double timeout, void (*callback) (int fd, int revents)) {
+ int nfds, n, i;
+
+ nfds = cleanup_polls ();
+
+ if (!nfds) return;
+
+ n = poll (pfd, nfds, ceil(timeout * 1000));
+ if (n < 0) {
+ if (errno == EINTR) return;
+ error ("poll");
+ }
+
+ for (i = 0; n && i < num_polls; i++) {
+ if (pfd[i].revents) {
+ callback (pfd[i].fd, pfd[i].revents);
+ n--;
+ }
+ }
+
+ return;
+}
+
diff --git a/traceroute/random.c b/traceroute/random.c
new file mode 100644
index 0000000..80fba9e
--- /dev/null
+++ b/traceroute/random.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/times.h>
+
+#include "traceroute.h"
+
+
+static void __init_random_seq (void) __attribute__ ((constructor));
+static void __init_random_seq (void) {
+
+ srand (times (NULL) + getpid ());
+}
+
+
+unsigned int random_seq (void) {
+
+ /* Not to worry about RANDOM_MAX and precision... */
+ return (rand () << 16) ^ (rand () << 8) ^ rand () ^ (rand () >> 8);
+}
+
diff --git a/traceroute/time.c b/traceroute/time.c
new file mode 100644
index 0000000..e4595a0
--- /dev/null
+++ b/traceroute/time.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "traceroute.h"
+
+
+/* Just returns current time as double, with most possible precision... */
+
+double get_time (void) {
+ struct timeval tv;
+ double d;
+
+ gettimeofday (&tv, NULL);
+
+ d = ((double) tv.tv_usec) / 1000000. + (unsigned long) tv.tv_sec;
+
+ return d;
+}
diff --git a/traceroute/traceroute.8 b/traceroute/traceroute.8
new file mode 100644
index 0000000..46074ad
--- /dev/null
+++ b/traceroute/traceroute.8
@@ -0,0 +1,629 @@
+.\" Copyright (c) 2006 Dmitry Butskoy (dmitry@butskoy.name)
+.\" License: GPL v2 or any later version
+.\" See COPYING for the status of this software
+.TH TRACEROUTE 8 "11 October 2006" "Traceroute" "Traceroute For Linux"
+.\" .UC 6
+.SH NAME
+traceroute \- print the route packets trace to network host
+.SH SYNOPSIS
+.na
+.BR traceroute " [" \-46dFITUnreAV "] [" "\-f first_ttl" "] [" "\-g gate,..." ]
+.br
+.ti +8
+.BR "" [ "-i device" "] [" "-m max_ttl" "] [" "-p port" "] [" "-s src_addr" ]
+.br
+.ti +8
+.BR "" [ "-q nqueries" "] [" "-N squeries" "] [" "-t tos" ]
+.br
+.ti +8
+.BR "" [ "-l flow_label" "] [" "-w waittimes" "] [" "-z sendwait" "] [" "-UL" "] [" "-D" ]
+.br
+.ti +8
+.BR "" [ "-P proto" "] [" "--sport=port" "] [" "-M method" "] [" "-O mod_options" ]
+.br
+.ti +8
+.BR "" [ "--mtu" "] [" "--back" ]
+.br
+.ti +8
+.BR host " [" "packet_len" "]"
+.br
+.BR traceroute6
+.RI " [" options ]
+.ad
+.SH DESCRIPTION
+.I traceroute
+tracks the route packets taken from an IP network on their
+way to a given host. It utilizes the IP protocol's time to live (TTL) field
+and attempts to elicit an ICMP TIME_EXCEEDED response from each gateway
+along the path to the host.
+.P
+.I traceroute6
+is equivalent to
+.I traceroute
+.B \-6
+.PP
+The only required parameter is the name or IP address of the
+destination
+.BR host \ .
+The optional
+.B packet_len\fR`gth
+is the total size of the probing packet (default 60 bytes
+for IPv4 and 80 for IPv6). The specified size can be ignored
+in some situations or increased up to a minimal value.
+.PP
+This program attempts to trace the route an IP packet would follow to some
+internet host by launching probe
+packets with a small ttl (time to live) then listening for an
+ICMP "time exceeded" reply from a gateway. We start our probes
+with a ttl of one and increase by one until we get an ICMP "port
+unreachable" (or TCP reset), which means we got to the "host", or hit a max (which
+defaults to 30 hops). Three probes (by default) are sent at each ttl setting
+and a line is printed showing the ttl, address of the gateway and
+round trip time of each probe. The address can be followed by additional
+information when requested. If the probe answers come from
+different gateways, the address of each responding system will
+be printed. If there is no response within a certain timeout,
+an "*" (asterisk) is printed for that probe.
+.PP
+After the trip time, some additional annotation can be printed:
+.BR !H ,
+.BR !N ,
+or
+.B !P
+(host, network or protocol unreachable),
+.B !S
+(source route failed),
+.B !F
+(fragmentation needed),
+.B !X
+(communication administratively prohibited),
+.B !V
+(host precedence violation),
+.B !C
+(precedence cutoff in effect), or
+.B !<num>
+(ICMP unreachable code <num>).
+If almost all the probes result in some kind of unreachable, traceroute
+will give up and exit.
+.PP
+We don't want the destination host to process the UDP probe packets,
+so the destination port is set to an unlikely value (you can change it with the
+.B \-p
+flag). There is no such a problem for ICMP or TCP tracerouting (for TCP we
+use half-open technique, which prevents our probes to be seen by applications
+on the destination host).
+.PP
+In the modern network environment the traditional traceroute methods
+can not be always applicable, because of widespread use of firewalls.
+Such firewalls filter the "unlikely" UDP ports, or even ICMP echoes.
+To solve this, some additional tracerouting methods are implemented
+(including tcp), see
+.B LIST OF AVAILABLE METHODS
+below. Such methods try to use particular protocol
+and source/destination port, in order to bypass firewalls (to be seen
+by firewalls just as a start of allowed type of a network session).
+.SH OPTIONS
+.TP
+.BI \--help
+Print help info and exit.
+.TP
+.BR \-4 ", " \-6
+Explicitly force IPv4 or IPv6 tracerouting. By default, the program
+will try to resolve the name given, and choose the appropriate
+protocol automatically. If resolving a host name returns both
+IPv4 and IPv6 addresses,
+.I traceroute
+will use IPv4.
+.TP
+.B \-I, \-\-icmp
+Use ICMP ECHO for probes
+.TP
+.B \-T, \-\-tcp
+Use TCP SYN for probes
+.TP
+.B \-d, --debug
+Enable socket level debugging (when the Linux kernel supports it)
+.TP
+.B \-F, --dont-fragment
+Do not fragment probe packets. (For IPv4 it also sets DF bit, which tells
+intermediate routers not to fragment remotely as well).
+.br
+
+.br
+Varying the size of the probing packet by the
+.B packet_len
+command line parameter, you can manually obtain information
+about the MTU of individual network hops. The
+.B \--mtu
+option (see below) tries to do this automatically.
+.br
+
+.br
+Note, that non-fragmented features (like
+.B \-F
+or
+.B \--mtu\fR)
+work properly since the Linux kernel 2.6.22 only.
+Before that version, IPv6 was always fragmented, IPv4 could use
+the once the discovered final mtu only (from the route cache), which can be
+less than the actual mtu of a device.
+.TP
+.BI \-f " first_ttl" ", --first=" first_ttl
+Specifies with what TTL to start. Defaults to 1.
+.TP
+.BI \-g " gateway" ", --gateway=" gateway
+Tells traceroute to add an IP source routing option to the outgoing
+packet that tells the network to route the packet through the
+specified
+.IR gateway
+(most routers have disabled source routing for security reasons).
+In general, several
+.IR gateway\fR's
+is allowed (comma separated). For IPv6, the form of
+.IR num\fB,\fIaddr\fB,\fIaddr...
+is allowed, where
+.IR num
+is a route header type (default is type 2). Note the type 0 route header
+is now deprecated (rfc5095).
+.TP
+.BI \-i " interface" ", --interface=" interface
+Specifies the interface through which
+.I traceroute
+should send packets. By default, the interface is selected
+according to the routing table.
+.TP
+.BI \-m " max_ttl" ", --max-hops=" max_ttl
+Specifies the maximum number of hops (max time-to-live value)
+.I traceroute
+will probe. The default is 30.
+.TP
+.BI \-N " squeries" ", --sim-queries=" squeries
+Specifies the number of probe packets sent out simultaneously.
+Sending several probes concurrently can speed up
+.I traceroute
+considerably. The default value is 16.
+.br
+Note that some routers and hosts can use ICMP rate throttling. In such
+a situation specifying too large number can lead to loss of some responses.
+.TP
+.BI \-n
+Do not try to map IP addresses to host names when displaying them.
+.TP
+.BI \-p " port" ", --port=" port
+For UDP tracing, specifies the destination port base
+.I traceroute
+will use (the destination port number will be incremented by each probe).
+.br
+For ICMP tracing, specifies the initial ICMP sequence value (incremented
+by each probe too).
+.br
+For TCP and others specifies just the (constant) destination
+port to connect.
+.TP
+.BI \-t " tos" ", --tos=" tos
+For IPv4, set the Type of Service (TOS) and Precedence value. Useful values
+are 16 (low delay) and 8 (high throughput). Note that in order to use
+some TOS precedence values, you have to be super user.
+.br
+For IPv6, set the Traffic Control value.
+.TP
+.BI \-l " flow_label" ", --flowlabel=" flow_label
+Use specified flow_label for IPv6 packets.
+.TP
+.BI \-w " max\fR[\fB,\fIhere\fB,\fInear\fR]" ", --wait=" max\fR[\fB,\fIhere\fB,\fInear\fR]
+Determines how long to wait for a response to a probe.
+.br
+
+.br
+There are three (in general) float values separated by a comma
+(or a slash).
+.IR Max
+specifies the maximum time (in seconds, default 5.0) to wait, in any case.
+.br
+
+.br
+Traditional traceroute implementation always waited whole
+.IR max
+seconds for any probe. But if we already have some replies from the
+.B same
+hop, or even from some
+.B next
+hop, we can use the round trip time of such a reply as a hint
+to determine the actual reasonable amount of time to wait.
+.br
+
+.br
+The optional
+.IR here
+(default 3.0) specifies a factor to multiply the round trip time of an already
+received response from the
+.B same
+hop. The resulting value is used as a timeout for the probe, instead of
+(but no more than)
+.IR max\fR.
+The optional
+.IR near
+(default 10.0) specifies a similar factor for a response from some
+.B next
+hop.
+(The time of the first found result is used in both cases).
+.br
+
+.br
+First, we look for the
+.B same
+hop (of the probe which will be printed first from now).
+If nothing found, then look for some
+.B next
+hop. If nothing found, use
+.IR max\fR.
+If
+.IR here
+and/or
+.IR near
+have zero values, the corresponding computation is skipped.
+.br
+.IR Here
+and
+.IR near
+are always set to zero if only
+.IR max
+is specified (for compatibility with previous versions).
+.TP
+.BI \-q " nqueries" ", --queries=" nqueries
+Sets the number of probe packets per hop. The default is 3.
+.TP
+.BI \-r
+Bypass the normal routing tables and send directly to a host on
+an attached network. If the host is not on a directly-attached
+network, an error is returned. This option can be used to ping a
+local host through an interface that has no route through it.
+.TP
+.BI \-s " source_addr" ", --source=" source_addr
+Chooses an alternative source address. Note that you must select the
+address of one of the interfaces.
+By default, the address of the outgoing interface is used.
+.TP
+.BI \-z " sendwait" ", --sendwait=" sendwait
+Minimal time interval between probes (default 0).
+If the value is more than 10, then it specifies a number in milliseconds,
+else it is a number of seconds (float point values allowed too).
+Useful when some routers use rate-limit for ICMP messages.
+.TP
+.B \-e, \-\-extensions
+Show ICMP extensions (rfc4884). The general form is
+.I CLASS\fB/\fITYPE\fB:
+followed by a hexadecimal dump.
+The MPLS (rfc4950) is shown parsed, in a form:
+.B MPLS:L=\fIlabel\fB,E=\fIexp_use\fB,S=\fIstack_bottom\fB,T=\fITTL
+(more objects separated by
+.B /
+). The Interface Information (rfc5837) is shown parsed as well, in a following form:
+.B \fR{\fBINC\fR|\fBSUB\fR|\fBOUT\fR|\fBNXT\fR}\fB:\fIindex\fB,\fIIP_addr\fB,"\fIname\fB",mtu=\fIMTU\fB
+(all four fields may be missing).
+.TP
+.B \-A, \-\-as\-path\-lookups
+Perform AS path lookups in routing registries and print results
+directly after the corresponding addresses.
+.TP
+.B \-V, \-\-version
+Print the version and exit.
+.br
+.P
+There are additional options intended for advanced usage
+(such as alternate trace methods etc.):
+.TP
+.B \--sport\fR=\fIport
+Chooses the source port to use. Implies
+.B \-N\ 1\fR\ -w\ 5 .
+Normally source ports (if applicable) are chosen by the system.
+.TP
+.B \--fwmark\fR=\fImark
+Set the firewall mark for outgoing packets (since the Linux kernel 2.6.25).
+.TP
+.BI \-M " method" ", --module=" name
+Use specified method for traceroute operations. Default traditional udp method
+has name
+.IR default ,
+icmp
+.BR "" ( "-I" ) "
+and tcp
+.BR "" ( "-T" ) "
+have names
+.I icmp
+and
+.I tcp
+respectively.
+.br
+Method-specific options can be passed by
+.BR \-O\ .
+Most methods have their simple shortcuts,
+.BR "" ( "-I " means " -M icmp" ,
+etc).
+.TP
+.BI \-O " option" ", --options=" options
+Specifies some method-specific option. Several options are separated by comma (or use several
+.B \-O
+on cmdline).
+Each method may have its own specific options, or many not have them at all.
+To print information about available options, use
+.BR \-O\ help .
+.TP
+.B \-U, \-\-udp
+Use UDP to particular destination port for tracerouting (instead of increasing
+the port per each probe). Default port is 53 (dns).
+.TP
+.BI \-UL
+Use UDPLITE for tracerouting (default port is 53).
+.TP
+.B \-D, \-\-dccp
+Use DCCP Requests for probes.
+.TP
+.BI \-P " protocol" ", --protocol=" protocol
+Use raw packet of specified protocol for tracerouting. Default protocol is
+253 (rfc3692).
+.TP
+.BI \--mtu
+Discover MTU along the path being traced. Implies
+.BR \-F\ \-N\ 1 .
+New
+.I mtu
+is printed once in a form of
+.B F=\fINUM
+at the first probe of a hop which requires such
+.I mtu
+to be reached. (Actually, the correspond "frag needed" icmp message
+normally is sent by the previous hop).
+.br
+
+.br
+Note, that some routers might cache once the seen information
+on a fragmentation. Thus you can receive the final mtu from a closer hop.
+Try to specify an unusual
+.I tos
+by
+.B \-t
+, this can help for one attempt (then it can be cached there as well).
+.br
+See
+.B \-F
+option for more info.
+.TP
+.BI \--back
+Print the number of backward hops when it seems different with the forward
+direction. This number is guessed in assumption that remote hops send reply
+packets with initial ttl set to either 64, or 128 or 255 (which seems
+a common practice). It is printed as a negate value in a form of '-NUM' .
+.SH LIST OF AVAILABLE METHODS
+In general, a particular traceroute method may have to be chosen by
+.BR \-M\ name ,
+but most of the methods have their simple cmdline switches
+(you can see them after the method name, if present).
+.SS default
+The traditional, ancient method of tracerouting. Used by default.
+.P
+Probe packets are udp datagrams with so-called "unlikely" destination ports.
+The "unlikely" port of the first probe is 33434, then for each next probe
+it is incremented by one. Since the ports are expected to be unused,
+the destination host normally returns "icmp unreach port" as a final response.
+(Nobody knows what happens when some application listens for such ports,
+though).
+.P
+This method is allowed for unprivileged users.
+.SS icmp \ \ \ \-I
+Most usual method for now, which uses icmp echo packets for probes.
+.br
+If you can ping(8) the destination host, icmp tracerouting is applicable
+as well.
+.P
+This method may be allowed for unprivileged users
+since the kernel 3.0 (IPv4, for IPv6 since 3.11), which supports new
+.I dgram icmp
+(or
+.IR \fR"\fIping\fR")
+sockets. To allow such sockets, sysadmin should provide
+.I net/ipv4/ping_group_range
+sysctl range to match any group of the user.
+.br
+Options:
+.TP
+.B raw
+Use only raw sockets (the traditional way).
+.br
+This way is tried first by default (for compatibility reasons),
+then new dgram icmp sockets as fallback.
+.TP
+.B dgram
+Use only dgram icmp sockets.
+.SS tcp \ \ \ \ \-T
+Well-known modern method, intended to bypass firewalls.
+.br
+Uses the constant destination port (default is 80, http).
+.P
+If some filters are present in the network path, then most probably
+any "unlikely" udp ports (as for
+.I default
+method) or even icmp echoes (as for
+.IR icmp )
+are filtered, and whole tracerouting will just stop at such a firewall.
+To bypass a network filter, we have to use only allowed protocol/port
+combinations. If we trace for some, say, mailserver, then more likely
+.B \-T \-p 25
+can reach it, even when
+.B \-I
+can not.
+.P
+This method uses well-known "half-open technique", which prevents
+applications on the destination host from seeing our probes at all.
+Normally, a tcp syn is sent. For non-listened ports we receive tcp reset,
+and all is done. For active listening ports we receive tcp syn+ack, but
+answer by tcp reset (instead of expected tcp ack), this way the remote tcp
+session is dropped even without the application ever taking notice.
+.P
+There is a couple of options for
+.I tcp
+method:
+.TP
+.B syn,ack,fin,rst,psh,urg,ece,cwr
+Sets specified tcp flags for probe packet, in any combination.
+.TP
+.B flags\fR=\fInum
+Sets the flags field in the tcp header exactly to
+.IR num .
+.TP
+.B ecn
+Send syn packet with tcp flags ECE and CWR (for Explicit Congestion
+Notification, rfc3168).
+.TP
+.B sack,timestamps,window_scaling
+Use the corresponding tcp header option in the outgoing probe packet.
+.TP
+.B sysctl
+Use current sysctl
+.IR "" ( "/proc/sys/net/*" )
+setting for the tcp header options above and
+.BR ecn .
+Always set by default, if nothing else specified.
+.TP
+.B fastopen
+Use fastopen tcp option (when
+.BR syn ),
+for initial cookie negotiation only.
+.TP
+.B mss\fR=[\fInum\fR]
+Use value of
+.I num
+(or unchanged) for maxseg tcp header option (when
+.BR syn ),
+and discover its clamping along the path being traced.
+New changed
+.I mss
+is printed once in a form of
+.B M=\fINUM
+at the first probe on which it was detected.
+Note, some routers may return too short original fragment
+in the time exceeded message, making the check impossible.
+Besides that the responses may come in a different order.
+All this can lead to a later place of the report
+(using
+.BR \-N\ 1
+can help for the order).
+.TP
+.B info
+Print tcp flags and supported options
+of final tcp replies when the target host is reached.
+Allows to determine whether an application listens the port and
+other useful things.
+Supported tcp options are all that can be set by
+.BR \-T\ \-O ,
+ie.
+.IR mss ,
+.IR sack ,
+.IR timestamps ,
+.IR window_scaling
+and
+.IR fastopen ,
+with the similar output format (a value for
+.IR mss
+and just presence for others).
+.P
+Default options is
+.BR syn,sysctl .
+.SS tcpconn
+An initial implementation of tcp method, simple using connect(2) call,
+which does full tcp session opening. Not recommended for normal use, because
+a destination application is always affected (and can be confused).
+.SS udp \ \ \ \ \-U
+Use udp datagram with constant destination port (default 53, dns).
+.br
+Intended to bypass firewall as well.
+.P
+Note, that unlike in
+.I tcp
+method, the correspond application on the destination host
+.B always
+receive our probes (with random data), and most can easily be confused
+by them. Most cases it will not respond to our packets though, so we will never
+see the final hop in the trace. (Fortunately, it seems that at least
+dns servers replies with something angry).
+.P
+This method is allowed for unprivileged users.
+.SS udplite \ \ \-UL
+Use udplite datagram for probes (with constant destination port,
+default 53).
+.P
+This method is allowed for unprivileged users.
+.br
+Options:
+.TP
+.B coverage\fR=\fInum
+Set udplite send coverage to
+.IR num .
+.SS dccp \ \ \-D
+Use DCCP Request packets for probes (rfc4340).
+.P
+This method uses the same "half-open technique" as used for TCP.
+The default destination port is 33434.
+.P
+Options:
+.TP
+.B service\fR=\fInum
+Set DCCP service code to
+.IR num
+(default is 1885957735).
+.SS raw \ \ \ \ \-P proto
+Send raw packet of protocol
+.IR proto .
+.br
+No protocol-specific headers are used, just IP header only.
+.br
+Implies
+.B \-N\ 1\fR\ -w\ 5 .
+.br
+Options:
+.TP
+.B protocol\fR=\fIproto
+Use IP protocol
+.I proto
+(default 253).
+.SH NOTES
+.PP
+To speed up work, normally several probes are sent simultaneously.
+On the other hand, it creates a "storm of packages", especially
+in the reply direction. Routers can throttle the rate of icmp responses,
+and some of replies can be lost. To avoid this, decrease the number
+of simultaneous probes, or even set it to 1 (like in initial traceroute
+implementation), i.e.
+.B \-N 1
+.PP
+The final (target) host can drop some of the simultaneous probes,
+and might even answer only the latest ones. It can lead to extra
+"looks like expired" hops near the final hop. We use a smart algorithm
+to auto-detect such a situation, but if it cannot help in your case, just use
+.B \-N 1
+too.
+.PP
+For even greater stability you can slow down the program's work by
+.B \-z
+option, for example use
+.B \-z 0.5
+for half-second pause between probes.
+.PP
+To avoid an extra waiting, we use adaptive algorithm for timeouts (see
+.B \-w
+option for more info). It can lead to premature expiry
+(especially when response times differ at times) and printing "*"
+instead of a time. In such a case, switch this algorithm off, by specifying
+.B \-w
+with the desired timeout only (for example,
+.B \-w 5\fR).
+.PP
+If some hops report nothing for every method, the last chance to obtain
+something is to use
+.B ping -R
+command (IPv4, and for nearest 8 hops only).
+.SH SEE ALSO
+.BR ping (8),
+.BR ping6 (8),
+.BR tcpdump (8),
+.BR netstat (8)
diff --git a/traceroute/traceroute.c b/traceroute/traceroute.c
new file mode 100644
index 0000000..08b850a
--- /dev/null
+++ b/traceroute/traceroute.c
@@ -0,0 +1,1693 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netdb.h>
+#include <errno.h>
+#include <locale.h>
+#include <sys/utsname.h>
+#include <linux/types.h>
+#include <linux/errqueue.h>
+
+/* XXX: Remove this when things will be defined properly in netinet/ ... */
+#include "flowlabel.h"
+
+#include <clif.h>
+#include "version.h"
+#include "traceroute.h"
+
+
+#ifndef ICMP6_DST_UNREACH_BEYONDSCOPE
+#ifdef ICMP6_DST_UNREACH_NOTNEIGHBOR
+#define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR
+#else
+#define ICMP6_DST_UNREACH_BEYONDSCOPE 2
+#endif
+#endif
+
+#ifndef IPV6_RECVHOPLIMIT
+#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
+#endif
+
+#ifndef IP_PMTUDISC_PROBE
+#define IP_PMTUDISC_PROBE 3
+#endif
+
+#ifndef IPV6_PMTUDISC_PROBE
+#define IPV6_PMTUDISC_PROBE 3
+#endif
+
+#ifndef AI_IDN
+#define AI_IDN 0
+#endif
+
+#ifndef NI_IDN
+#define NI_IDN 0
+#endif
+
+
+#define MAX_HOPS 255
+#define MAX_PROBES 10
+#define MAX_GATEWAYS_4 8
+#define MAX_GATEWAYS_6 127
+#define DEF_HOPS 30
+#define DEF_SIM_PROBES 16 /* including several hops */
+#define DEF_NUM_PROBES 3
+#define DEF_WAIT_SECS 5.0
+#define DEF_HERE_FACTOR 3
+#define DEF_NEAR_FACTOR 10
+#ifndef DEF_WAIT_PREC
+#define DEF_WAIT_PREC 0.001 /* +1 ms to avoid precision issues */
+#endif
+#define DEF_SEND_SECS 0
+#define DEF_DATA_LEN 40 /* all but IP header... */
+#define MAX_PACKET_LEN 65000
+#ifndef DEF_AF
+#define DEF_AF AF_INET
+#endif
+
+#define ttl2hops(X) (((X) <= 64 ? 65 : ((X) <= 128 ? 129 : 256)) - (X))
+
+
+static char version_string[] = "Modern traceroute for Linux, "
+ "version " _TEXT(VERSION)
+ "\nCopyright (c) 2016 Dmitry Butskoy, "
+ " License: GPL v2 or any later";
+static int debug = 0;
+static unsigned int first_hop = 1;
+static unsigned int max_hops = DEF_HOPS;
+static unsigned int sim_probes = DEF_SIM_PROBES;
+static unsigned int probes_per_hop = DEF_NUM_PROBES;
+
+static char **gateways = NULL;
+static int num_gateways = 0;
+static unsigned char *rtbuf = NULL;
+static size_t rtbuf_len = 0;
+static unsigned int ipv6_rthdr_type = 2; /* IPV6_RTHDR_TYPE_2 */
+
+static size_t header_len = 0;
+static size_t data_len = 0;
+
+static int dontfrag = 0;
+static int noresolve = 0;
+static int extension = 0;
+static int as_lookups = 0;
+static unsigned int dst_port_seq = 0;
+static unsigned int tos = 0;
+static unsigned int flow_label = 0;
+static int noroute = 0;
+static unsigned int fwmark = 0;
+static int packet_len = -1;
+static double wait_secs = DEF_WAIT_SECS;
+static double here_factor = DEF_HERE_FACTOR;
+static double near_factor = DEF_NEAR_FACTOR;
+static double send_secs = DEF_SEND_SECS;
+static int mtudisc = 0;
+static int backward = 0;
+
+static sockaddr_any dst_addr = {{ 0, }, };
+static char *dst_name = NULL;
+static char *device = NULL;
+static sockaddr_any src_addr = {{ 0, }, };
+static unsigned int src_port = 0;
+
+static const char *module = "default";
+static const tr_module *ops = NULL;
+
+static char *opts[16] = { NULL, }; /* assume enough */
+static unsigned int opts_idx = 1; /* first one reserved... */
+
+
+static int af = 0;
+
+static probe *probes = NULL;
+static unsigned int num_probes = 0;
+
+
+static void ex_error (const char *format, ...) {
+ va_list ap;
+
+ va_start (ap, format);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+
+ fprintf (stderr, "\n");
+
+ exit (2);
+}
+
+void error (const char *str) {
+
+ fprintf (stderr, "\n");
+
+ perror (str);
+
+ exit (1);
+}
+
+void error_or_perm (const char *str) {
+
+ if (errno == EPERM)
+ fprintf (stderr, "You do not have enough privileges to use "
+ "this traceroute method.");
+ error (str);
+}
+
+
+void put_err (probe *pb, const char *format, ...) {
+ va_list ap;
+ char *curr = pb->err_str;
+ char *end = pb->err_str + sizeof (pb->err_str) - 1;
+
+ /* It can already contain something when `--mtu' or `-T -O mss' */
+ while (curr < end && *curr) curr++;
+
+ va_start (ap, format);
+ vsnprintf (curr, end - curr, format, ap);
+ va_end (ap);
+}
+
+
+/* Set initial parameters according to how we was called */
+
+static void check_progname (const char *name) {
+ const char *p;
+ int l;
+
+ p = strrchr (name, '/');
+ if (p) p++;
+ else p = name;
+
+ l = strlen (p);
+ if (l <= 0) return;
+ l--;
+
+ if (p[l] == '6') af = AF_INET6;
+ else if (p[l] == '4') af = AF_INET;
+
+ if (!strncmp (p, "tcp", 3))
+ module = "tcp";
+ if (!strncmp (p, "tracert", 7))
+ module = "icmp";
+
+ return;
+}
+
+
+static int getaddr (const char *name, sockaddr_any *addr) {
+ int ret;
+ struct addrinfo hints, *ai, *res = NULL;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = af;
+ hints.ai_flags = AI_IDN;
+
+ ret = getaddrinfo (name, NULL, &hints, &res);
+ if (ret) {
+ fprintf (stderr, "%s: %s\n", name, gai_strerror (ret));
+ return -1;
+ }
+
+ for (ai = res; ai; ai = ai->ai_next) {
+ if (ai->ai_family == af) break;
+ /* when af not specified, choose DEF_AF if present */
+ if (!af && ai->ai_family == DEF_AF)
+ break;
+ }
+ if (!ai) ai = res; /* anything... */
+
+ if (ai->ai_addrlen > sizeof (*addr))
+ return -1; /* paranoia */
+ memcpy (addr, ai->ai_addr, ai->ai_addrlen);
+
+ freeaddrinfo (res);
+
+ /* No v4mapped addresses in real network, interpret it as ipv4 anyway */
+ if (addr->sa.sa_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED (&addr->sin6.sin6_addr)
+ ) {
+ if (af == AF_INET6) return -1;
+ addr->sa.sa_family = AF_INET;
+ addr->sin.sin_addr.s_addr = addr->sin6.sin6_addr.s6_addr32[3];
+ }
+
+ return 0;
+}
+
+
+static void make_fd_used (int fd) {
+ int nfd;
+
+ if (fcntl (fd, F_GETFL) != -1)
+ return;
+
+ if (errno != EBADF)
+ error ("fcntl F_GETFL");
+
+ nfd = open ("/dev/null", O_RDONLY);
+ if (nfd < 0) error ("open /dev/null");
+
+ if (nfd != fd) {
+ dup2 (nfd, fd);
+ close (nfd);
+ }
+
+ return;
+}
+
+
+static char addr2str_buf[INET6_ADDRSTRLEN];
+
+const char *addr2str (const sockaddr_any *addr) {
+
+ getnameinfo (&addr->sa, sizeof (*addr),
+ addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST);
+
+ return addr2str_buf;
+}
+
+
+/* IP options stuff */
+
+static void init_ip_options (void) {
+ sockaddr_any *gates;
+ int i, max;
+
+ if (!num_gateways)
+ return;
+
+ /* check for TYPE,ADDR,ADDR... form */
+ if (af == AF_INET6 && num_gateways > 1 && gateways[0]) {
+ char *q;
+ unsigned int value = strtoul (gateways[0], &q, 0);
+
+ if (!*q) {
+ ipv6_rthdr_type = value;
+ num_gateways--;
+ for (i = 0; i < num_gateways; i++)
+ gateways[i] = gateways[i + 1];
+ }
+ }
+
+
+ max = af == AF_INET ? MAX_GATEWAYS_4 : MAX_GATEWAYS_6;
+ if (num_gateways > max)
+ ex_error ("Too many gateways specified. No more than %d", max);
+
+
+ gates = alloca (num_gateways * sizeof (*gates));
+
+ for (i = 0; i < num_gateways; i++) {
+
+ if (!gateways[i]) error ("strdup");
+
+ if (getaddr (gateways[i], &gates[i]) < 0)
+ ex_error (""); /* already reported */
+ if (gates[i].sa.sa_family != af)
+ ex_error ("IP versions mismatch in gateway addresses");
+
+ free (gateways[i]);
+ }
+
+ free (gateways);
+ gateways = NULL;
+
+
+ if (af == AF_INET) {
+ struct in_addr *in;
+
+ rtbuf_len = 4 + (num_gateways + 1) * sizeof (*in);
+ rtbuf = malloc (rtbuf_len);
+ if (!rtbuf) error ("malloc");
+
+ in = (struct in_addr *) &rtbuf[4];
+ for (i = 0; i < num_gateways; i++)
+ memcpy (&in[i], &gates[i].sin.sin_addr, sizeof (*in));
+ /* final hop */
+ memcpy (&in[i], &dst_addr.sin.sin_addr, sizeof (*in));
+ i++;
+
+ rtbuf[0] = IPOPT_NOP;
+ rtbuf[1] = IPOPT_LSRR;
+ rtbuf[2] = (i * sizeof (*in)) + 3;
+ rtbuf[3] = IPOPT_MINOFF;
+
+ }
+ else if (af == AF_INET6) {
+ struct in6_addr *in6;
+ struct ip6_rthdr *rth;
+
+ /* IPV6_RTHDR_TYPE_0 length is 8 */
+ rtbuf_len = 8 + num_gateways * sizeof (*in6);
+ rtbuf = malloc (rtbuf_len);
+ if (!rtbuf) error ("malloc");
+
+ rth = (struct ip6_rthdr *) rtbuf;
+ rth->ip6r_nxt = 0;
+ rth->ip6r_len = 2 * num_gateways;
+ rth->ip6r_type = ipv6_rthdr_type;
+ rth->ip6r_segleft = num_gateways;
+
+ *((uint32_t *) (rth + 1)) = 0;
+
+ in6 = (struct in6_addr *) (rtbuf + 8);
+ for (i = 0; i < num_gateways; i++)
+ memcpy (&in6[i], &gates[i].sin6.sin6_addr, sizeof (*in6));
+ }
+
+ return;
+}
+
+
+/* Command line stuff */
+
+static int set_af (CLIF_option *optn, char *arg) {
+ int vers = (long) optn->data;
+
+ if (vers == 4) af = AF_INET;
+ else if (vers == 6) af = AF_INET6;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int add_gateway (CLIF_option *optn, char *arg) {
+
+ if (num_gateways >= MAX_GATEWAYS_6) { /* 127 > 8 ... :) */
+ fprintf (stderr, "Too many gateways specified.");
+ return -1;
+ }
+
+ gateways = realloc (gateways, (num_gateways + 1) * sizeof (*gateways));
+ if (!gateways) error ("malloc");
+ gateways[num_gateways++] = strdup (arg);
+
+ return 0;
+}
+
+static int set_source (CLIF_option *optn, char *arg) {
+
+ return getaddr (arg, &src_addr);
+}
+
+static int set_port (CLIF_option *optn, char *arg) {
+ unsigned int *up = (unsigned int *) optn->data;
+ char *q;
+
+ *up = strtoul (arg, &q, 0);
+ if (q == arg) {
+ struct servent *s = getservbyname (arg, NULL);
+
+ if (!s) return -1;
+ *up = ntohs (s->s_port);
+ }
+
+ return 0;
+}
+
+static int set_module (CLIF_option *optn, char *arg) {
+
+ module = (char *) optn->data;
+
+ return 0;
+}
+
+
+static int set_mod_option (CLIF_option *optn, char *arg) {
+
+ if (!strcmp (arg, "help")) {
+ const tr_module *mod = tr_get_module (module);
+
+ if (mod && mod->options) {
+ /* just to set common keyword flag... */
+ CLIF_parse (1, &arg, 0, 0, CLIF_KEYWORD);
+ CLIF_print_options (NULL, mod->options);
+ } else
+ fprintf (stderr, "No options for module `%s'\n", module);
+
+ exit (0);
+ }
+
+ if (opts_idx >= sizeof (opts) / sizeof (*opts)) {
+ fprintf (stderr, "Too many module options\n");
+ return -1;
+ }
+
+ opts[opts_idx] = strdup (arg);
+ if (!opts[opts_idx]) error ("strdup");
+ opts_idx++;
+
+ return 0;
+}
+
+
+static int set_raw (CLIF_option *optn, char *arg) {
+ char buf[1024];
+
+ module = "raw";
+
+ snprintf (buf, sizeof (buf), "protocol=%s", arg);
+ return set_mod_option (optn, buf);
+}
+
+
+static int set_wait_specs (CLIF_option *optn, char *arg) {
+ char *p, *q;
+
+ here_factor = near_factor = 0;
+
+ wait_secs = strtod (p = arg, &q);
+ if (q == p) return -1;
+ if (!*q++) return 0;
+
+ here_factor = strtod (p = q, &q);
+ if (q == p) return -1;
+ if (!*q++) return 0;
+
+ near_factor = strtod (p = q, &q);
+ if (q == p || *q) return -1;
+
+ return 0;
+}
+
+
+static int set_host (CLIF_argument *argm, char *arg, int index) {
+
+ if (getaddr (arg, &dst_addr) < 0)
+ return -1;
+
+ dst_name = arg;
+
+ /* i.e., guess it by the addr in cmdline... */
+ if (!af) af = dst_addr.sa.sa_family;
+
+ return 0;
+}
+
+
+static CLIF_option option_list[] = {
+ { "4", 0, 0, "Use IPv4", set_af, (void *) 4, 0, CLIF_EXTRA },
+ { "6", 0, 0, "Use IPv6", set_af, (void *) 6, 0, 0 },
+ { "d", "debug", 0, "Enable socket level debugging",
+ CLIF_set_flag, &debug, 0, 0 },
+ { "F", "dont-fragment", 0, "Do not fragment packets",
+ CLIF_set_flag, &dontfrag, 0, CLIF_ABBREV },
+ { "f", "first", "first_ttl", "Start from the %s hop (instead from 1)",
+ CLIF_set_uint, &first_hop, 0, 0 },
+ { "g", "gateway", "gate", "Route packets through the specified gateway "
+ "(maximum " _TEXT(MAX_GATEWAYS_4) " for IPv4 and "
+ _TEXT(MAX_GATEWAYS_6) " for IPv6)",
+ add_gateway, 0, 0, CLIF_SEVERAL },
+ { "I", "icmp", 0, "Use ICMP ECHO for tracerouting",
+ set_module, "icmp", 0, 0 },
+ { "T", "tcp", 0, "Use TCP SYN for tracerouting (default "
+ "port is " _TEXT(DEF_TCP_PORT) ")",
+ set_module, "tcp", 0, 0 },
+ { "i", "interface", "device", "Specify a network interface "
+ "to operate with",
+ CLIF_set_string, &device, 0, 0 },
+ { "m", "max-hops", "max_ttl", "Set the max number of hops (max TTL "
+ "to be reached). Default is " _TEXT(DEF_HOPS) ,
+ CLIF_set_uint, &max_hops, 0, 0 },
+ { "N", "sim-queries", "squeries", "Set the number of probes "
+ "to be tried simultaneously (default is "
+ _TEXT(DEF_SIM_PROBES) ")",
+ CLIF_set_uint, &sim_probes, 0, 0 },
+ { "n", 0, 0, "Do not resolve IP addresses to their domain names",
+ CLIF_set_flag, &noresolve, 0, 0 },
+ { "p", "port", "port", "Set the destination port to use. "
+ "It is either initial udp port value for "
+ "\"default\" method (incremented by each probe, "
+ "default is " _TEXT(DEF_START_PORT) "), "
+ "or initial seq for \"icmp\" (incremented as well, "
+ "default from 1), or some constant destination port"
+ " for other methods (with default of "
+ _TEXT(DEF_TCP_PORT) " for \"tcp\", "
+ _TEXT(DEF_UDP_PORT) " for \"udp\", etc.)",
+ set_port, &dst_port_seq, 0, 0 },
+ { "t", "tos", "tos", "Set the TOS (IPv4 type of service) or TC "
+ "(IPv6 traffic class) value for outgoing packets",
+ CLIF_set_uint, &tos, 0, 0 },
+ { "l", "flowlabel", "flow_label", "Use specified %s for IPv6 packets",
+ CLIF_set_uint, &flow_label, 0, 0 },
+ { "w", "wait", "MAX,HERE,NEAR", "Wait for a probe no more than HERE "
+ "(default " _TEXT(DEF_HERE_FACTOR) ") times longer "
+ "than a response from the same hop, or no more "
+ "than NEAR (default " _TEXT(DEF_NEAR_FACTOR) ") "
+ "times than some next hop, or MAX (default "
+ _TEXT(DEF_WAIT_SECS) ") seconds "
+ "(float point values allowed too)",
+ set_wait_specs, 0, 0, 0 },
+ { "q", "queries", "nqueries", "Set the number of probes per each hop. "
+ "Default is " _TEXT(DEF_NUM_PROBES),
+ CLIF_set_uint, &probes_per_hop, 0, 0 },
+ { "r", 0, 0, "Bypass the normal routing and send directly to a host "
+ "on an attached network",
+ CLIF_set_flag, &noroute, 0, 0 },
+ { "s", "source", "src_addr", "Use source %s for outgoing packets",
+ set_source, 0, 0, 0 },
+ { "z", "sendwait", "sendwait", "Minimal time interval between probes "
+ "(default " _TEXT(DEF_SEND_SECS) "). If the value "
+ "is more than 10, then it specifies a number "
+ "in milliseconds, else it is a number of seconds "
+ "(float point values allowed too)",
+ CLIF_set_double, &send_secs, 0, 0 },
+ { "e", "extensions", 0, "Show ICMP extensions (if present), "
+ "including MPLS",
+ CLIF_set_flag, &extension, 0, CLIF_ABBREV },
+ { "A", "as-path-lookups", 0, "Perform AS path lookups in routing "
+ "registries and print results directly after "
+ "the corresponding addresses",
+ CLIF_set_flag, &as_lookups, 0, 0 },
+ { "M", "module", "name", "Use specified module (either builtin or "
+ "external) for traceroute operations. Most methods "
+ "have their shortcuts (`-I' means `-M icmp' etc.)",
+ CLIF_set_string, &module, 0, CLIF_EXTRA },
+ { "O", "options", "OPTS", "Use module-specific option %s for the "
+ "traceroute module. Several %s allowed, separated "
+ "by comma. If %s is \"help\", print info about "
+ "available options",
+ set_mod_option, 0, 0, CLIF_SEVERAL | CLIF_EXTRA },
+ { 0, "sport", "num", "Use source port %s for outgoing packets. "
+ "Implies `-N 1'",
+ set_port, &src_port, 0, CLIF_EXTRA },
+#ifdef SO_MARK
+ { 0, "fwmark", "num", "Set firewall mark for outgoing packets",
+ CLIF_set_uint, &fwmark, 0, 0 },
+#endif
+ { "U", "udp", 0, "Use UDP to particular port for tracerouting "
+ "(instead of increasing the port per each probe), "
+ "default port is " _TEXT(DEF_UDP_PORT),
+ set_module, "udp", 0, CLIF_EXTRA },
+ { 0, "UL", 0, "Use UDPLITE for tracerouting (default dest port is "
+ _TEXT(DEF_UDP_PORT) ")",
+ set_module, "udplite", 0, CLIF_ONEDASH|CLIF_EXTRA },
+ { "D", "dccp", 0, "Use DCCP Request for tracerouting (default "
+ "port is " _TEXT(DEF_DCCP_PORT) ")",
+ set_module, "dccp", 0, CLIF_EXTRA },
+ { "P", "protocol", "prot", "Use raw packet of protocol %s "
+ "for tracerouting",
+ set_raw, 0, 0, CLIF_EXTRA },
+ { 0, "mtu", 0, "Discover MTU along the path being traced. "
+ "Implies `-F -N 1'",
+ CLIF_set_flag, &mtudisc, 0, CLIF_EXTRA },
+ { 0, "back", 0, "Guess the number of hops in the backward path "
+ "and print if it differs",
+ CLIF_set_flag, &backward, 0, CLIF_EXTRA },
+ CLIF_VERSION_OPTION (version_string),
+ CLIF_HELP_OPTION,
+ CLIF_END_OPTION
+};
+
+static CLIF_argument arg_list[] = {
+ { "host", "The host to traceroute to",
+ set_host, 0, CLIF_STRICT },
+ { "packetlen", "The full packet length (default is the length of "
+ "an IP header plus " _TEXT(DEF_DATA_LEN) "). Can be "
+ "ignored or increased to a minimal allowed value",
+ CLIF_arg_int, &packet_len, 0 },
+ CLIF_END_ARGUMENT
+};
+
+
+static void do_it (void);
+
+int main (int argc, char *argv[]) {
+
+ setlocale (LC_ALL, "");
+ setlocale (LC_NUMERIC, "C"); /* avoid commas in msec printed */
+
+ check_progname (argv[0]);
+
+
+ if (CLIF_parse (argc, argv, option_list, arg_list,
+ CLIF_MAY_JOIN_ARG | CLIF_HELP_EMPTY) < 0
+ ) exit (2);
+
+
+ ops = tr_get_module (module);
+ if (!ops) ex_error ("Unknown traceroute module %s", module);
+
+
+ if (!first_hop || first_hop > max_hops)
+ ex_error ("first hop out of range");
+ if (max_hops > MAX_HOPS)
+ ex_error ("max hops cannot be more than " _TEXT(MAX_HOPS));
+ if (!probes_per_hop || probes_per_hop > MAX_PROBES)
+ ex_error ("no more than " _TEXT(MAX_PROBES) " probes per hop");
+ if (wait_secs < 0 || here_factor < 0 || near_factor < 0)
+ ex_error ("bad wait specifications `%g,%g,%g' used",
+ wait_secs, here_factor, near_factor);
+ if (packet_len > MAX_PACKET_LEN)
+ ex_error ("too big packetlen %d specified", packet_len);
+ if (src_addr.sa.sa_family && src_addr.sa.sa_family != af)
+ ex_error ("IP version mismatch in addresses specified");
+ if (send_secs < 0)
+ ex_error ("bad sendtime `%g' specified", send_secs);
+ if (send_secs >= 10) /* it is milliseconds */
+ send_secs /= 1000;
+
+ if (af == AF_INET6 && (tos || flow_label))
+ dst_addr.sin6.sin6_flowinfo =
+ htonl (((tos & 0xff) << 20) | (flow_label & 0x000fffff));
+
+ if (src_port) {
+ src_addr.sin.sin_port = htons ((uint16_t) src_port);
+ src_addr.sa.sa_family = af;
+ }
+
+ if (src_port || ops->one_per_time) {
+ sim_probes = 1;
+ here_factor = near_factor = 0;
+ }
+
+
+ /* make sure we don't std{in,out,err} to open sockets */
+ make_fd_used (0);
+ make_fd_used (1);
+ make_fd_used (2);
+
+
+ init_ip_options ();
+
+ header_len = (af == AF_INET ? sizeof (struct iphdr)
+ : sizeof (struct ip6_hdr)) +
+ rtbuf_len + ops->header_len;
+
+ if (mtudisc) {
+ dontfrag = 1;
+ sim_probes = 1;
+ if (packet_len < 0)
+ packet_len = MAX_PACKET_LEN;
+ }
+
+ if (packet_len < 0) {
+ if (DEF_DATA_LEN >= ops->header_len)
+ data_len = DEF_DATA_LEN - ops->header_len;
+ } else {
+ if (packet_len >= header_len)
+ data_len = packet_len - header_len;
+ }
+
+
+ num_probes = max_hops * probes_per_hop;
+ probes = calloc (num_probes, sizeof (*probes));
+ if (!probes) error ("calloc");
+
+
+ if (ops->options && opts_idx > 1) {
+ opts[0] = strdup (module); /* aka argv[0] ... */
+ if (CLIF_parse (opts_idx, opts, ops->options, 0, CLIF_KEYWORD) < 0)
+ exit (2);
+ }
+
+ if (ops->init (&dst_addr, dst_port_seq, &data_len) < 0)
+ ex_error ("trace method's init failed");
+
+
+ do_it ();
+
+ return 0;
+}
+
+
+/* PRINT STUFF */
+
+static void print_header (void) {
+
+ /* Note, without ending new-line! */
+ printf ("traceroute to %s (%s), %u hops max, %zu byte packets",
+ dst_name, addr2str (&dst_addr), max_hops,
+ header_len + data_len);
+ fflush (stdout);
+}
+
+
+static void print_addr (sockaddr_any *res) {
+ const char *str;
+
+ if (!res->sa.sa_family)
+ return;
+
+ str = addr2str (res);
+
+
+ if (noresolve)
+ printf (" %s", str);
+ else {
+ char buf[1024];
+
+ buf[0] = '\0';
+ getnameinfo (&res->sa, sizeof (*res), buf, sizeof (buf),
+ 0, 0, NI_IDN);
+ printf (" %s (%s)", buf[0] ? buf : str, str);
+ }
+
+ if (as_lookups)
+ printf (" [%s]", get_as_path (str));
+}
+
+
+static void print_probe (probe *pb) {
+ unsigned int idx = (pb - probes);
+ unsigned int ttl = idx / probes_per_hop + 1;
+ unsigned int np = idx % probes_per_hop;
+
+ if (np == 0)
+ printf ("\n%2u ", ttl);
+
+
+ if (!pb->res.sa.sa_family)
+ printf (" *");
+ else {
+ int prn = !np; /* print if the first... */
+
+ if (np) { /* ...and if differs with previous */
+ probe *p;
+
+ /* skip expired */
+ for (p = pb - 1; np && !p->res.sa.sa_family; p--, np--) ;
+
+ if (!np ||
+ !equal_addr (&p->res, &pb->res) ||
+ (p->ext != pb->ext &&
+ !(p->ext && pb->ext && !strcmp (p->ext, pb->ext))) ||
+ (backward && p->recv_ttl != pb->recv_ttl)
+ ) prn = 1;
+ }
+
+ if (prn) {
+ print_addr (&pb->res);
+
+ if (pb->ext) printf (" <%s>", pb->ext);
+
+ if (backward && pb->recv_ttl) {
+ int hops = ttl2hops (pb->recv_ttl);
+ if (hops != ttl) printf (" '-%d'", hops);
+ }
+ }
+ }
+
+
+ if (pb->recv_time) {
+ double diff = pb->recv_time - pb->send_time;
+
+ printf (" %.3f ms", diff * 1000);
+ }
+
+ if (pb->err_str[0])
+ printf (" %s", pb->err_str);
+
+
+ fflush (stdout);
+
+ return;
+}
+
+
+static void print_end (void) {
+
+ printf ("\n");
+}
+
+
+/* Compute timeout stuff */
+
+static double get_timeout (probe *pb) {
+ double value;
+
+ if (here_factor) {
+ /* check for already replied from the same hop */
+ int i, idx = (pb - probes);
+ probe *p = &probes[idx - (idx % probes_per_hop)];
+
+ for (i = 0; i < probes_per_hop; i++, p++) {
+ /* `p == pb' skipped since !pb->done */
+
+ if (p->done && (value = p->recv_time - p->send_time) > 0) {
+ value += DEF_WAIT_PREC;
+ value *= here_factor;
+ return value < wait_secs ? value : wait_secs;
+ }
+ }
+ }
+
+ if (near_factor) {
+ /* check forward for already replied */
+ probe *p, *endp = probes + num_probes;
+
+ for (p = pb + 1; p < endp && p->send_time; p++) {
+
+ if (p->done && (value = p->recv_time - p->send_time) > 0) {
+ value += DEF_WAIT_PREC;
+ value *= near_factor;
+ return value < wait_secs ? value : wait_secs;
+ }
+ }
+ }
+
+ return wait_secs;
+}
+
+
+/* Check expiration stuff */
+
+static void check_expired (probe *pb) {
+ int idx = (pb - probes);
+ probe *p, *endp = probes + num_probes;
+ probe *fp = NULL, *pfp = NULL;
+
+ if (!pb->done) /* an ops method still not release it */
+ return;
+
+
+ /* check all the previous in the same hop */
+ for (p = &probes[idx - (idx % probes_per_hop)]; p < pb; p++) {
+
+ if (!p->done || /* too early to decide something */
+ !p->final /* already ttl-exceeded in the same hop */
+ ) return;
+
+ pfp = p; /* some of the previous probes is final */
+ }
+
+ /* check forward all the sent probes */
+ for (p = pb + 1; p < endp && p->send_time; p++) {
+
+ if (p->done) { /* some next probe already done... */
+ if (!p->final) /* ...was ttl-exceeded. OK, we are expired. */
+ return;
+ else {
+ fp = p;
+ break;
+ }
+ }
+ }
+
+ if (!fp) /* no any final probe found. Assume expired. */
+ return;
+
+
+ /* Well. There is a situation "*(this) * * * * ... * * final"
+ We cannot guarantee that "final" is in its right place.
+ We've sent "sim_probes" simultaneously, and the final hop
+ can drop some of them and answer only for latest ones.
+ If we can detect/assume that it so, then just put "final"
+ to the (pseudo-expired) "this" place.
+ */
+
+ /* It seems that the case of "answers for latest ones only"
+ occurs mostly with icmp_unreach error answers ("!H" etc.).
+ Icmp_echoreply, tcp_reset and even icmp_port_unreach looks
+ like going in the right order.
+ */
+ if (!fp->err_str[0]) /* not an icmp_unreach error report... */
+ return;
+
+
+ if (pfp ||
+ (idx % probes_per_hop) + (fp - pb) < probes_per_hop
+ ) {
+ /* Either some previous (pfp) or some next probe
+ in this hop is final. It means that the whole hop is final.
+ Do the replace (it also causes further "final"s to be shifted
+ here too).
+ */
+ goto replace_by_final;
+ }
+
+
+ /* If the final probe is an icmp_unreachable report
+ (either in a case of some error, like "!H", or just port_unreach),
+ it could follow the "time-exceed" report from the *same* hop.
+ */
+ for (p = pb - 1; p >= probes; p--) {
+ if (equal_addr (&p->res, &fp->res)) {
+ /* ...Yes. Put "final" to the "this" place. */
+ goto replace_by_final;
+ }
+ }
+
+
+ if (fp->recv_ttl) {
+ /* Consider the ttl value of the report packet and guess where
+ the "final" should be. If it seems that it should be
+ in the same hop as "this", then do replace.
+ */
+ int back_hops, ttl;
+
+ /* We assume that the reporting one has an initial ttl value
+ of either 64, or 128, or 255. It is most widely used
+ in the modern routers and computers.
+ The idea comes from tracepath(1) routine.
+ */
+ back_hops = ttl2hops (fp->recv_ttl);
+
+ /* It is possible that the back path differs from the forward
+ and therefore has different number of hops. To minimize
+ such an influence, get the nearest previous time-exceeded
+ probe and compare with it.
+ */
+ for (p = pb - 1; p >= probes; p--) {
+ if (p->done && !p->final && p->recv_ttl) {
+ int hops = ttl2hops (p->recv_ttl);
+
+ if (hops < back_hops) {
+ ttl = (p - probes) / probes_per_hop + 1;
+ back_hops = (back_hops - hops) + ttl;
+ break;
+ }
+ }
+ }
+
+ ttl = idx / probes_per_hop + 1;
+ if (back_hops == ttl)
+ /* Yes! It seems that "final" should be at "this" place */
+ goto replace_by_final;
+ else if (back_hops < ttl)
+ /* Hmmm... Assume better to replace here too... */
+ goto replace_by_final;
+
+ }
+
+
+ /* No idea what to do. Assume expired. */
+
+ return;
+
+
+replace_by_final:
+
+ *pb = *fp;
+
+ memset (fp, 0, sizeof (*fp));
+ /* block extra re-send */
+ fp->send_time = 1.;
+
+ return;
+}
+
+
+probe *probe_by_seq (int seq) {
+ int n;
+
+ if (seq <= 0) return NULL;
+
+ for (n = 0; n < num_probes; n++) {
+ if (probes[n].seq == seq)
+ return &probes[n];
+ }
+
+ return NULL;
+}
+
+probe *probe_by_sk (int sk) {
+ int n;
+
+ if (sk <= 0) return NULL;
+
+ for (n = 0; n < num_probes; n++) {
+ if (probes[n].sk == sk)
+ return &probes[n];
+ }
+
+ return NULL;
+}
+
+
+static void poll_callback (int fd, int revents) {
+
+ ops->recv_probe (fd, revents);
+}
+
+
+static void do_it (void) {
+ int start = (first_hop - 1) * probes_per_hop;
+ int end = num_probes;
+ double last_send = 0;
+
+ print_header ();
+
+
+ while (start < end) {
+ int n, num = 0;
+ double next_time = 0;
+ double now_time = get_time ();
+
+
+ for (n = start; n < end; n++) {
+ probe *pb = &probes[n];
+
+
+ if (n == start && /* probably time to print... */
+ !pb->done && pb->send_time /* ...but yet not replied */
+ ) {
+ double expire_time = pb->send_time + get_timeout (pb);
+
+ if (expire_time > now_time)
+ next_time = expire_time;
+ else {
+ ops->expire_probe (pb);
+ check_expired (pb);
+ }
+ }
+
+
+ if (pb->done) {
+
+ if (n == start) { /* can print it now */
+ print_probe (pb);
+ start++;
+ }
+
+ if (pb->final)
+ end = (n / probes_per_hop + 1) * probes_per_hop;
+
+ continue;
+ }
+
+
+ if (!pb->send_time) {
+ int ttl;
+ double next;
+
+ if (send_secs && (next = last_send + send_secs) > now_time) {
+ next_time = next;
+ break;
+ }
+
+ ttl = n / probes_per_hop + 1;
+
+ ops->send_probe (pb, ttl);
+
+ if (!pb->send_time) {
+ if (next_time) break; /* have chances later */
+ else error ("send probe");
+ }
+
+ last_send = pb->send_time;
+ }
+
+
+ if (!next_time)
+ next_time = pb->send_time + get_timeout (pb);
+
+ num++;
+ if (num >= sim_probes) break;
+ }
+
+
+ if (next_time) {
+ double timeout = next_time - get_time ();
+
+ if (timeout < 0) timeout = 0;
+
+ do_poll (timeout, poll_callback);
+ }
+
+ }
+
+
+ print_end ();
+
+ return;
+}
+
+
+void tune_socket (int sk) {
+ int i = 0;
+
+ if (debug) {
+ i = 1;
+ if (setsockopt (sk, SOL_SOCKET, SO_DEBUG, &i, sizeof (i)) < 0)
+ error ("setsockopt SO_DEBUG");
+ }
+
+
+#ifdef SO_MARK
+ if (fwmark) {
+ if (setsockopt (sk, SOL_SOCKET, SO_MARK,
+ &fwmark, sizeof (fwmark)) < 0
+ ) error ("setsockopt SO_MARK");
+ }
+#endif
+
+
+ if (rtbuf && rtbuf_len) {
+ if (af == AF_INET) {
+ if (setsockopt (sk, IPPROTO_IP, IP_OPTIONS,
+ rtbuf, rtbuf_len) < 0
+ ) error ("setsockopt IP_OPTIONS");
+ }
+ else if (af == AF_INET6) {
+ if (setsockopt (sk, IPPROTO_IPV6, IPV6_RTHDR,
+ rtbuf, rtbuf_len) < 0
+ ) error ("setsockopt IPV6_RTHDR");
+ }
+ }
+
+
+ bind_socket (sk);
+
+
+ if (af == AF_INET) {
+
+ i = dontfrag ? IP_PMTUDISC_PROBE : IP_PMTUDISC_DONT;
+ if (setsockopt (sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0 &&
+ (!dontfrag || (i = IP_PMTUDISC_DO,
+ setsockopt (sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0))
+ ) error ("setsockopt IP_MTU_DISCOVER");
+
+ if (tos) {
+ i = tos;
+ if (setsockopt (sk, SOL_IP, IP_TOS, &i, sizeof (i)) < 0)
+ error ("setsockopt IP_TOS");
+ }
+
+ }
+ else if (af == AF_INET6) {
+
+ i = dontfrag ? IPV6_PMTUDISC_PROBE : IPV6_PMTUDISC_DONT;
+ if (setsockopt (sk, SOL_IPV6, IPV6_MTU_DISCOVER,&i,sizeof(i)) < 0 &&
+ (!dontfrag || (i = IPV6_PMTUDISC_DO,
+ setsockopt (sk, SOL_IPV6, IPV6_MTU_DISCOVER,&i,sizeof(i)) < 0))
+ ) error ("setsockopt IPV6_MTU_DISCOVER");
+
+
+ if (flow_label) {
+ struct in6_flowlabel_req flr;
+
+ memset (&flr, 0, sizeof (flr));
+ flr.flr_label = htonl (flow_label & 0x000fffff);
+ flr.flr_action = IPV6_FL_A_GET;
+ flr.flr_flags = IPV6_FL_F_CREATE;
+ flr.flr_share = IPV6_FL_S_ANY;
+ memcpy (&flr.flr_dst, &dst_addr.sin6.sin6_addr,
+ sizeof (flr.flr_dst));
+
+ if (setsockopt (sk, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR,
+ &flr, sizeof (flr)) < 0
+ ) error ("setsockopt IPV6_FLOWLABEL_MGR");
+ }
+
+ if (tos) {
+ i = tos;
+ if (setsockopt (sk, IPPROTO_IPV6, IPV6_TCLASS,
+ &i, sizeof (i)) < 0
+ ) error ("setsockopt IPV6_TCLASS");
+ }
+
+ if (tos || flow_label) {
+ i = 1;
+ if (setsockopt (sk, IPPROTO_IPV6, IPV6_FLOWINFO_SEND,
+ &i, sizeof (i)) < 0
+ ) error ("setsockopt IPV6_FLOWINFO_SEND");
+ }
+ }
+
+
+ if (noroute) {
+ i = noroute;
+ if (setsockopt (sk, SOL_SOCKET, SO_DONTROUTE, &i, sizeof (i)) < 0)
+ error ("setsockopt SO_DONTROUTE");
+ }
+
+
+ use_timestamp (sk);
+
+ use_recv_ttl (sk);
+
+ fcntl (sk, F_SETFL, O_NONBLOCK);
+
+ return;
+}
+
+
+void parse_icmp_res (probe *pb, int type, int code, int info) {
+
+ if (af == AF_INET) {
+
+ if (type == ICMP_TIME_EXCEEDED) {
+ if (code == ICMP_EXC_TTL)
+ return;
+ }
+
+ if (type == ICMP_DEST_UNREACH) {
+
+ switch (code) {
+ case ICMP_UNREACH_NET:
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_ISOLATED:
+ case ICMP_UNREACH_TOSNET:
+ put_err (pb, "!N");
+ break;
+
+ case ICMP_UNREACH_HOST:
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ case ICMP_UNREACH_TOSHOST:
+ put_err (pb, "!H");
+ break;
+
+ case ICMP_UNREACH_NET_PROHIB:
+ case ICMP_UNREACH_HOST_PROHIB:
+ case ICMP_UNREACH_FILTER_PROHIB:
+ put_err (pb, "!X");
+ break;
+
+ case ICMP_UNREACH_PORT:
+ /* dest host is reached */
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ put_err (pb, "!P");
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ put_err (pb, "!F-%d", info);
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ put_err (pb, "!S");
+ break;
+
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ put_err (pb, "!V");
+ break;
+
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ put_err (pb, "!C");
+ break;
+
+ default:
+ put_err (pb, "!<%u>", code);
+ break;
+ }
+
+ } else
+ put_err (pb, "!<%u-%u>", type, code);
+
+ }
+ else if (af == AF_INET6) {
+
+ if (type == ICMP6_TIME_EXCEEDED) {
+ if (code == ICMP6_TIME_EXCEED_TRANSIT)
+ return;
+ }
+
+ if (type == ICMP6_DST_UNREACH) {
+
+ switch (code) {
+
+ case ICMP6_DST_UNREACH_NOROUTE:
+ put_err (pb, "!N");
+ break;
+
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ case ICMP6_DST_UNREACH_ADDR:
+ put_err (pb, "!H");
+ break;
+
+ case ICMP6_DST_UNREACH_ADMIN:
+ put_err (pb, "!X");
+ break;
+
+ case ICMP6_DST_UNREACH_NOPORT:
+ /* dest host is reached */
+ break;
+
+ default:
+ put_err (pb, "!<%u>", code);
+ break;
+ }
+ }
+ else if (type == ICMP6_PACKET_TOO_BIG)
+ put_err (pb, "!F-%d", info);
+ else
+ put_err (pb, "!<%u-%u>", type, code);
+ }
+
+ pb->final = 1;
+
+ return;
+}
+
+
+static void parse_local_res (probe *pb, int ee_errno, int info) {
+
+ if (ee_errno == EMSGSIZE && info != 0) {
+ put_err (pb, "!F-%d", info);
+ pb->final = 1;
+ return;
+ }
+
+ errno = ee_errno;
+ error ("local recverr");
+}
+
+
+void probe_done (probe *pb) {
+
+ if (pb->sk) {
+ del_poll (pb->sk);
+ close (pb->sk);
+ pb->sk = 0;
+ }
+
+ pb->seq = 0;
+
+ pb->done = 1;
+}
+
+
+void recv_reply (int sk, int err, check_reply_t check_reply) {
+ struct msghdr msg;
+ sockaddr_any from;
+ struct iovec iov;
+ int n;
+ probe *pb;
+ char buf[1280]; /* min mtu for ipv6 ( >= 576 for ipv4) */
+ char *bufp = buf;
+ char control[1024];
+ struct cmsghdr *cm;
+ double recv_time = 0;
+ int recv_ttl = 0;
+ struct sock_extended_err *ee = NULL;
+
+
+ memset (&msg, 0, sizeof (msg));
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof (from);
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof (control);
+ iov.iov_base = buf;
+ iov.iov_len = sizeof (buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+
+ n = recvmsg (sk, &msg, err ? MSG_ERRQUEUE : 0);
+ if (n < 0) return;
+
+
+ /* when not MSG_ERRQUEUE, AF_INET returns full ipv4 header
+ on raw sockets...
+ */
+
+ if (!err &&
+ af == AF_INET &&
+ /* XXX: Assume that the presence of an extra header means
+ that it is not a raw socket...
+ */
+ ops->header_len == 0
+ ) {
+ struct iphdr *ip = (struct iphdr *) bufp;
+ int hlen;
+
+ if (n < sizeof (struct iphdr)) return;
+
+ hlen = ip->ihl << 2;
+ if (n < hlen) return;
+
+ bufp += hlen;
+ n -= hlen;
+ }
+
+
+ pb = check_reply (sk, err, &from, bufp, n);
+ if (!pb) {
+
+ /* for `frag needed' case at the local host,
+ kernel >= 3.13 sends local error (no more icmp)
+ */
+ if (!n && err && dontfrag) {
+ pb = &probes[(first_hop - 1) * probes_per_hop];
+ if (pb->done) return;
+ } else
+ return;
+ }
+
+
+ /* Parse CMSG stuff */
+
+ for (cm = CMSG_FIRSTHDR (&msg); cm; cm = CMSG_NXTHDR (&msg, cm)) {
+ void *ptr = CMSG_DATA (cm);
+
+ if (cm->cmsg_level == SOL_SOCKET) {
+
+ if (cm->cmsg_type == SO_TIMESTAMP) {
+ struct timeval *tv = (struct timeval *) ptr;
+
+ recv_time = tv->tv_sec + tv->tv_usec / 1000000.;
+ }
+ }
+ else if (cm->cmsg_level == SOL_IP) {
+
+ if (cm->cmsg_type == IP_TTL)
+ recv_ttl = *((int *) ptr);
+ else if (cm->cmsg_type == IP_RECVERR) {
+
+ ee = (struct sock_extended_err *) ptr;
+
+ if (ee->ee_origin != SO_EE_ORIGIN_ICMP &&
+ ee->ee_origin != SO_EE_ORIGIN_LOCAL
+ ) return;
+
+ /* dgram icmp sockets might return extra things... */
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP &&
+ (ee->ee_type == ICMP_SOURCE_QUENCH ||
+ ee->ee_type == ICMP_REDIRECT)
+ ) return;
+ }
+ }
+ else if (cm->cmsg_level == SOL_IPV6) {
+
+ if (cm->cmsg_type == IPV6_HOPLIMIT)
+ recv_ttl = *((int *) ptr);
+ else if (cm->cmsg_type == IPV6_RECVERR) {
+
+ ee = (struct sock_extended_err *) ptr;
+
+ if (ee->ee_origin != SO_EE_ORIGIN_ICMP6 &&
+ ee->ee_origin != SO_EE_ORIGIN_LOCAL
+ ) return;
+ }
+ }
+ }
+
+ if (!recv_time)
+ recv_time = get_time ();
+
+
+ if (!err)
+ memcpy (&pb->res, &from, sizeof (pb->res));
+
+ pb->recv_time = recv_time;
+
+ pb->recv_ttl = recv_ttl;
+
+ if (ee && ee->ee_origin != SO_EE_ORIGIN_LOCAL) { /* icmp or icmp6 */
+ memcpy (&pb->res, SO_EE_OFFENDER (ee), sizeof(pb->res));
+ parse_icmp_res (pb, ee->ee_type, ee->ee_code, ee->ee_info);
+ }
+
+ if (ee && ee->ee_origin == SO_EE_ORIGIN_LOCAL)
+ parse_local_res (pb, ee->ee_errno, ee->ee_info);
+
+
+ if (ee &&
+ mtudisc &&
+ ee->ee_info >= header_len &&
+ ee->ee_info < header_len + data_len
+ ) {
+ data_len = ee->ee_info - header_len;
+
+ probe_done (pb);
+
+ /* clear this probe (as actually the previous hop answers here)
+ but fill its `err_str' by the info obtained. Ugly, but easy...
+ */
+ memset (pb, 0, sizeof (*pb));
+ put_err (pb, "F=%d", ee->ee_info);
+
+ return;
+ }
+
+
+ if (ee &&
+ extension &&
+ header_len + n >= (128 + 8) && /* at least... (rfc4884) */
+ header_len <= 128 && /* paranoia */
+ ((af == AF_INET && (ee->ee_type == ICMP_TIME_EXCEEDED ||
+ ee->ee_type == ICMP_DEST_UNREACH ||
+ ee->ee_type == ICMP_PARAMETERPROB)) ||
+ (af == AF_INET6 && (ee->ee_type == ICMP6_TIME_EXCEEDED ||
+ ee->ee_type == ICMP6_DST_UNREACH))
+ )
+ ) {
+ int step;
+ int offs = 128 - header_len;
+
+ if (n > data_len) step = 0; /* guaranteed at 128 ... */
+ else
+ step = af == AF_INET ? 4 : 8;
+
+ handle_extensions (pb, bufp + offs, n - offs, step);
+ }
+
+
+ probe_done (pb);
+}
+
+
+int equal_addr (const sockaddr_any *a, const sockaddr_any *b) {
+
+ if (!a->sa.sa_family)
+ return 0;
+
+ if (a->sa.sa_family != b->sa.sa_family)
+ return 0;
+
+ if (a->sa.sa_family == AF_INET6)
+ return !memcmp (&a->sin6.sin6_addr, &b->sin6.sin6_addr,
+ sizeof (a->sin6.sin6_addr));
+ else
+ return !memcmp (&a->sin.sin_addr, &b->sin.sin_addr,
+ sizeof (a->sin.sin_addr));
+ return 0; /* not reached */
+}
+
+
+void bind_socket (int sk) {
+ sockaddr_any *addr, tmp;
+
+ if (device) {
+ if (setsockopt (sk, SOL_SOCKET, SO_BINDTODEVICE,
+ device, strlen (device) + 1) < 0
+ ) error ("setsockopt SO_BINDTODEVICE");
+ }
+
+ if (!src_addr.sa.sa_family) {
+ memset (&tmp, 0, sizeof (tmp));
+ tmp.sa.sa_family = af;
+ addr = &tmp;
+ } else
+ addr = &src_addr;
+
+ if (bind (sk, &addr->sa, sizeof (*addr)) < 0)
+ error ("bind");
+
+ return;
+}
+
+
+void use_timestamp (int sk) {
+ int n = 1;
+
+ setsockopt (sk, SOL_SOCKET, SO_TIMESTAMP, &n, sizeof (n));
+ /* foo on errors... */
+}
+
+
+void use_recv_ttl (int sk) {
+ int n = 1;
+
+ if (af == AF_INET)
+ setsockopt (sk, SOL_IP, IP_RECVTTL, &n, sizeof (n));
+ else if (af == AF_INET6)
+ setsockopt (sk, SOL_IPV6, IPV6_RECVHOPLIMIT, &n, sizeof (n));
+ /* foo on errors */
+}
+
+
+void use_recverr (int sk) {
+ int val = 1;
+
+ if (af == AF_INET) {
+ if (setsockopt (sk, SOL_IP, IP_RECVERR, &val, sizeof (val)) < 0)
+ error ("setsockopt IP_RECVERR");
+ }
+ else if (af == AF_INET6) {
+ if (setsockopt (sk, SOL_IPV6, IPV6_RECVERR, &val, sizeof (val)) < 0)
+ error ("setsockopt IPV6_RECVERR");
+ }
+}
+
+
+void set_ttl (int sk, int ttl) {
+
+ if (af == AF_INET) {
+ if (setsockopt (sk, SOL_IP, IP_TTL, &ttl, sizeof (ttl)) < 0)
+ error ("setsockopt IP_TTL");
+ }
+ else if (af == AF_INET6) {
+ if (setsockopt (sk, SOL_IPV6, IPV6_UNICAST_HOPS,
+ &ttl, sizeof (ttl)) < 0
+ ) error ("setsockopt IPV6_UNICAST_HOPS");
+ }
+}
+
+
+int do_send (int sk, const void *data, size_t len, const sockaddr_any *addr) {
+ int res;
+
+ if (!addr || raw_can_connect ())
+ res = send (sk, data, len, 0);
+ else
+ res = sendto (sk, data, len, 0, &addr->sa, sizeof (*addr));
+
+ if (res < 0) {
+ if (errno == ENOBUFS || errno == EAGAIN)
+ return res;
+ if (errno == EMSGSIZE || errno == EHOSTUNREACH)
+ return 0; /* recverr will say more... */
+ error ("send"); /* not recoverable */
+ }
+
+ return res;
+}
+
+
+/* There is a bug in the kernel before 2.6.25, which prevents icmp errors
+ to be obtained by MSG_ERRQUEUE for ipv6 connected raw sockets.
+*/
+static int can_connect = -1;
+
+#define VER(A,B,C,D) (((((((A) << 8) | (B)) << 8) | (C)) << 8) | (D))
+
+int raw_can_connect (void) {
+
+ if (can_connect < 0) {
+
+ if (af == AF_INET)
+ can_connect = 1;
+ else { /* AF_INET6 */
+ struct utsname uts;
+ int n;
+ unsigned int a, b, c, d = 0;
+
+ if (uname (&uts) < 0)
+ return 0;
+
+ n = sscanf (uts.release, "%u.%u.%u.%u", &a, &b, &c, &d);
+ can_connect = (n >= 3 && VER (a, b, c, d) >= VER (2, 6, 25, 0));
+ }
+ }
+
+ return can_connect;
+}
diff --git a/traceroute/traceroute.h b/traceroute/traceroute.h
new file mode 100644
index 0000000..0c3b974
--- /dev/null
+++ b/traceroute/traceroute.h
@@ -0,0 +1,109 @@
+/*
+ Copyright (c) 2006, 2007 Dmitry Butskoy
+ <dmitry@butskoy.name>
+ License: GPL v2 or any later
+
+ See COPYING for the status of this software.
+*/
+
+#include <netinet/in.h>
+
+#include <clif.h>
+
+
+union common_sockaddr {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+};
+typedef union common_sockaddr sockaddr_any;
+
+struct probe_struct {
+ int done;
+ int final;
+ sockaddr_any res;
+ double send_time;
+ double recv_time;
+ int recv_ttl;
+ int sk;
+ int seq;
+ char *ext;
+ char err_str[32]; /* assume enough */
+};
+typedef struct probe_struct probe;
+
+
+struct tr_module_struct {
+ struct tr_module_struct *next;
+ const char *name;
+ int (*init) (const sockaddr_any *dest,
+ unsigned int port_seq, size_t *packet_len);
+ void (*send_probe) (probe *pb, int ttl);
+ void (*recv_probe) (int fd, int revents);
+ void (*expire_probe) (probe *pb);
+ CLIF_option *options; /* per module options, if any */
+ int one_per_time; /* no simultaneous probes */
+ size_t header_len; /* additional header length (aka for udp) */
+};
+typedef struct tr_module_struct tr_module;
+
+
+#define __TEXT(X) #X
+#define _TEXT(X) __TEXT(X)
+
+#define DEF_START_PORT 33434 /* start for traditional udp method */
+#define DEF_UDP_PORT 53 /* dns */
+#define DEF_TCP_PORT 80 /* web */
+#define DEF_DCCP_PORT DEF_START_PORT /* is it a good choice?... */
+#define DEF_RAW_PROT 253 /* for experimentation and testing, rfc3692 */
+
+
+void error (const char *str) __attribute__ ((noreturn));
+void error_or_perm (const char *str) __attribute__ ((noreturn));
+void put_err (probe *pb, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+const char *addr2str (const sockaddr_any *addr);
+
+double get_time (void);
+void tune_socket (int sk);
+void parse_icmp_res (probe *pb, int type, int code, int info);
+void probe_done (probe *pb);
+
+typedef probe *(*check_reply_t) (int sk, int err, sockaddr_any *from,
+ char *buf, size_t len);
+void recv_reply (int sk, int err, check_reply_t check_reply);
+
+int equal_addr (const sockaddr_any *a, const sockaddr_any *b);
+
+probe *probe_by_seq (int seq);
+probe *probe_by_sk (int sk);
+
+void bind_socket (int sk);
+void use_timestamp (int sk);
+void use_recv_ttl (int sk);
+void use_recverr (int sk);
+void set_ttl (int sk, int ttl);
+int do_send (int sk, const void *data, size_t len, const sockaddr_any *addr);
+
+void add_poll (int fd, int events);
+void del_poll (int fd);
+void do_poll (double timeout, void (*callback) (int fd, int revents));
+
+void handle_extensions (probe *pb, char *buf, int len, int step);
+const char *get_as_path (const char *query);
+
+int raw_can_connect (void);
+
+unsigned int random_seq (void);
+uint16_t in_csum (const void *ptr, size_t len);
+
+
+void tr_register_module (tr_module *module);
+const tr_module *tr_get_module (const char *name);
+
+#define TR_MODULE(MOD) \
+static void __init_ ## MOD (void) __attribute__ ((constructor)); \
+static void __init_ ## MOD (void) { \
+ \
+ tr_register_module (&MOD); \
+}
+
diff --git a/wrappers/Makefile b/wrappers/Makefile
new file mode 100644
index 0000000..53935f6
--- /dev/null
+++ b/wrappers/Makefile
@@ -0,0 +1,15 @@
+#
+# Copyright (c) 2007 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Nothing to do.
+#
+
+all depend install:
+ @true
+
diff --git a/wrappers/README.wrappers b/wrappers/README.wrappers
new file mode 100644
index 0000000..bd0e2f4
--- /dev/null
+++ b/wrappers/README.wrappers
@@ -0,0 +1,18 @@
+
+This directory contains various wrappers for traceroute, which provide
+some compatibility for some another traceroute-like software,
+including tcptraceroute(8).
+
+These wrappers try to emulate the command-line interface only.
+The actual output is the output of the underlain traceroute(8).
+
+Some of options and features certainly are not supported
+(especially if it is considered too extra :) ).
+
+The idea of such wrappers is mostly not wrappers itself. It is to inspect
+the features implemented by competitors, whether it is already supported by us,
+or it is useful to be supported, or it is not useful or excessive.
+
+We have no plans to implement all features of all existing software.
+Let the people a chance to play with something... ;)
+
diff --git a/wrappers/lft b/wrappers/lft
new file mode 100755
index 0000000..a6cb089
--- /dev/null
+++ b/wrappers/lft
@@ -0,0 +1,117 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Dmitry K. Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Shell wrapper providing lft(8) command line interface.
+#
+# The original implementation of lft(8) can be obtained
+# from http://pwhois.org/lft/
+#
+
+
+prgname=$0
+opts=""
+length=""
+method="-T"
+
+dport=""
+sport=""
+queries=2
+ahead=5
+scatter=20
+timeout=1000
+
+
+usage () {
+ echo "Usage: $prgname [-ACEFINRSTUVbehinpruvz] [-d dport] [-s sport]
+ [-m retry min] [-M retry max] [-a ahead] [-c scatter ms] [-t timeout ms]
+ [-l min ttl] [-H max ttl] [-L length] [-q ISN] [-D device] [--help]
+ [gateway ...] target:dport" >&2
+}
+
+warning () {
+ echo "$prgname: Option '$1' is not implemented in this wrapper" >&2
+}
+
+
+PARSED=`getopt -o 'ACEFINRSTUVbehinpruvzd:s:m:M:a:c:t:l:H:L:q:D:' -l help -- "$@"`
+[ $? != 0 ] && exit 2
+
+eval set -- "$PARSED"
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ -d) dport=$2; shift 2 ;;
+ -s) sport=$2; shift 2 ;;
+ -z) sport=""; shift ;;
+ -m) queries=$2; shift 2 ;;
+ -a) ahead=$2; shift 2 ;;
+ -c) scatter=$2; shift 2 ;;
+ -t) timeout=$2; shift 2 ;;
+ -l) opts="$opts -f $2"; shift 2 ;;
+ -L) length=$2; shift 2 ;;
+ -D) case "$2" in
+ [0-9]*) opts="$opts -s $2" ;;
+ *) opts="$opts -i $2" ;;
+ esac
+ shift 2
+ ;;
+ -H) opts="$opts -m $2"; shift 2 ;;
+ -I) opts="$opts -t 0x10"; shift ;;
+ -n) opts="$opts -n"; shift ;;
+ -h) shift ;;
+ -F) opts="$opts -O fin"; shift ;;
+ -u) method="-U"; shift ;;
+ -N) opts="$opts -A"; shift ;;
+ -p) method="-I"; shift ;;
+ -b) shift ;;
+ -A) opts="$opts -A"; shift ;;
+ -[ieErCTUV]) warning $1; shift ;;
+ -[Mq]) warning $1; shift 2 ;;
+ -[RS]) shift ;;
+ --help) usage; exit 0 ;;
+ -v) echo "\"lft\"-compatible wrapper for new Linux Traceroute" >&2;
+ exit 0 ;;
+ --) shift; break ;;
+ *) echo "$prgname: Internal parsing error" >&2; exit 2 ;;
+ esac
+done
+
+while [ $# -gt 1 ]
+do
+ opts="$opts -g $1"
+ shift
+done
+
+[ $# -eq 0 ] && {
+ usage
+ exit 2
+}
+
+case "$1" in
+ *:*:*) host=$1 ;;
+ *:*) dport=${1##*:}; host=${1%:*} ;;
+ *) host=$1 ;;
+esac
+
+[ -n "$dport" ] && opts="$opts -p $dport"
+[ -n "$sport" ] && opts="$opts --sport=$sport"
+opts="$opts -q $queries"
+opts="$opts -N $(($ahead * $queries))"
+opts="$opts -z $scatter"
+
+timeout=`printf "%04d" $timeout`
+sec=${timeout%???}
+opts="$opts -w $sec.${timeout#$sec}"
+
+opts="$method $opts"
+
+exec traceroute $opts $host $length
+
diff --git a/wrappers/tcptraceroute b/wrappers/tcptraceroute
new file mode 100755
index 0000000..6abe4bf
--- /dev/null
+++ b/wrappers/tcptraceroute
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Shell wrapper providing tcptraceroute(8) command line interface.
+#
+# The original implementation of tcptraceroute(8) can be obtained
+# from http://michael.toren.net/code/tcptraceroute/
+#
+
+opts="-T -O info"
+length=""
+prgname=$0
+
+
+usage () {
+ echo "Usage: $prgname [-hvnFSAE] [-i dev] [-f furst_ttl] [-l length]
+ [-q nqueries] [-t tos] [-m max_ttl] [-p src_port] [-s src_addr]
+ [-w wait_time] host [dest_port] [length]" >&2
+}
+
+
+PARSED=`getopt -- 'hvdnNi:l:f:Fm:p:q:w:s:t:SAE' "$@"`
+[ $? != 0 ] && exit 2
+
+eval set -- "$PARSED"
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ -[dnF]) opts="$opts $1"; shift ;;
+ -N) shift ;;
+ -[ifmqwst]) opts="$opts $1 $2"; shift 2 ;;
+ -l) length=$2; shift 2 ;;
+ -p) opts="$opts --sport=$2"; shift 2 ;;
+ -S) opts="$opts -O syn"; shift ;;
+ -A) opts="$opts -O ack"; shift ;;
+ -E) opts="$opts -O ecn"; shift ;;
+ -h) usage ; exit 0 ;;
+ -v) echo "\"tcptraceroute\"-compatible wrapper for new Linux Traceroute" >&2;
+ exit 0 ;;
+ --) shift; break ;;
+ *) echo "$prgname: Internal parsing error" >&2; exit 2 ;;
+ esac
+done
+
+[ $# -eq 0 ] && {
+ usage
+ exit 2
+}
+
+host=$1
+shift
+
+[ $# -gt 0 ] && {
+ opts="$opts -p $1"
+ shift
+}
+
+[ $# -gt 0 ] && {
+ length=$1
+ shift
+}
+
+
+# Say to the people it is actually another program...
+echo "Running:
+ traceroute $opts $host $length" >&2
+
+
+exec traceroute $opts $host $length
+
diff --git a/wrappers/tcptraceroute.8 b/wrappers/tcptraceroute.8
new file mode 100644
index 0000000..7c19ecf
--- /dev/null
+++ b/wrappers/tcptraceroute.8
@@ -0,0 +1,34 @@
+.\" Copyright (c) 2016 Dmitry Butskoy (dmitry@butskoy.name)
+.\" License: GPL v2 or any later version
+.\" See COPYING for the status of this software
+.TH TCPTRACEROUTE 8 "8 March 2016" "Traceroute" "Traceroute For Linux"
+.\" .UC 6
+.SH NAME
+tcptraceroute \- print the route packets trace to network host
+.SH SYNOPSIS
+.na
+.BR tcptraceroute
+.RI " [" options ]
+.ad
+.SH DESCRIPTION
+.I tcptraceroute
+is just a link to the system
+.B traceroute\fR,
+to allow run it without specifying
+.B \-T
+option each time (for switch to the TCP method). It is fully equivalent to
+.B traceroute \-T\fR,
+the rest of the command line is the same.
+.br
+See
+.BR traceroute (8)
+for more info.
+.SH NOTES
+There was an original implementation of tcptraceroute, which had some options
+differ (but rare used). Most common
+.BR "" [ "dnFi:f:m:q:w:s:t:" ]
+was exactly the same, but
+.BR "" [ "l:p:NSAE" ]
+was not. For full compatibility, a wrapper provided (available in the source code).
+.SH SEE ALSO
+.BR traceroute (8)
diff --git a/wrappers/tracepath b/wrappers/tracepath
new file mode 100755
index 0000000..c7d3770
--- /dev/null
+++ b/wrappers/tracepath
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Dmitry Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Shell wrapper providing tracepath(1) or tracepath6(1) command line interface.
+#
+# The original implementation of tracepath/tracepath6 can be obtained
+# from the iputils package, available at http://www.skbuff.net/iputils/
+#
+
+opts="-q1 --mtu --back"
+port=44444
+prgname=$0
+length=""
+
+
+usage () {
+ echo "Usage: $prgname [-nbh] [ -l pktlen ] host[/port]" >&2
+}
+
+
+PARSED=`getopt -- 'hnbl:' "$@"`
+[ $? != 0 ] && exit 2
+
+eval set -- "$PARSED"
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ -n) opts="$opts $1"; shift ;;
+ -b) shift ;;
+ -l) length=$2; shift 2 ;;
+ -h) usage ; exit 0 ;;
+ --) shift; break ;;
+ *) echo "$prgname: Internal parsing error" >&2; exit 2 ;;
+ esac
+done
+
+[ $# -eq 0 ] && {
+ usage
+ exit 2
+}
+
+host=$1
+case "$host" in
+ */*) port=${host##*/}
+ host=${host%/*}
+ ;;
+esac
+
+
+case "$prgname" in
+ *6) opts="$opts -6" ;;
+esac
+
+exec traceroute $opts -p $port $host $length
+
diff --git a/wrappers/traceproto b/wrappers/traceproto
new file mode 100755
index 0000000..0a03b2d
--- /dev/null
+++ b/wrappers/traceproto
@@ -0,0 +1,127 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Dmitry K. Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Shell wrapper providing traceproto(8) command line interface.
+#
+# The original implementation of traceproto(8) can be obtained
+# from http://traceproto.sourceforge.net/
+#
+
+
+prgname=$0
+opts=""
+length=""
+method="tcp"
+sendwait=100
+cont=""
+iface=$TP_DEFAULT_IF
+
+
+usage () {
+ echo "Usage: $prgname [-cCTfAhvR] [-p protocol] [-d dst_port] [-D max_dst_port]
+ [-s src_port] [-S max_src_port] [-m min_ttl] [-M max_ttl] [-w response_timeout]
+ [-W send_delay] [-a account_level] [-P payload_size]
+ [-F interface] [-k skips] [-I consecutive_trace_count]
+ [-H packets_per_hop] [-i incr_pattern] [-o output_style] [-t tcp_flags]
+ target" >&2
+}
+
+warning () {
+ echo "$prgname: Option '$1' is not implemented in this wrapper" >&2
+}
+
+
+PARSED=`getopt -- 'cCTfAhvRp:d:D:s:S:m:M:w:W:a:P:F:k:I:H:i:o:t:' "$@"`
+[ $? != 0 ] && exit 2
+
+eval set -- "$PARSED"
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ -p) method=$2; shift 2 ;;
+ -d) opts="$opts -p $2"; shift 2 ;;
+ -s) opts="$opts --sport=$2"; shift 2 ;;
+ -m) opts="$opts -f $2"; shift 2 ;;
+ -M) opts="$opts -m $2"; shift 2 ;;
+ -w) opts="$opts -w $2"; shift 2 ;;
+ -W) sendwait=$2; shift 2 ;;
+ -P) length=$2; shift 2 ;;
+ -c) cont=100000; shift ;;
+ -I) cont=$2; shift 2 ;;
+ -H) opts="$opts -q $2"; shift 2 ;;
+ -f) opts="$opts -F"; shift ;;
+ -F) iface=$2; shift 2 ;;
+ -A) opts="$opts -A"; shift ;;
+ -o) [ $2 != "c" ] && warning $1; shift 2 ;;
+ -t) case $2 in
+ *S*) opts="$opts -O syn" ;;
+ esac
+ case $2 in
+ *A*) opts="$opts -O ack" ;;
+ esac
+ case $2 in
+ *R*) opts="$opts -O rst" ;;
+ esac
+ case $2 in
+ *U*) opts="$opts -O urg" ;;
+ esac
+ case $2 in
+ *P*) opts="$opts -O psh" ;;
+ esac
+ case $2 in
+ *F*) opts="$opts -O fin" ;;
+ esac
+ case $2 in
+ *E*) opts="$opts -O ece" ;;
+ esac
+ case $2 in
+ *C*) opts="$opts -O cwr" ;;
+ esac
+ shift 2 ;;
+ -[DSaki]) warning $1; shift 2 ;;
+ -[TCR]) warning $1; shift ;;
+ -h) usage; exit 0 ;;
+ -v) echo "\"traceproto\"-compatible wrapper for new Linux Traceroute" >&2;
+ exit 0 ;;
+ --) shift; break ;;
+ *) echo "$prgname: Internal parsing error" >&2; exit 2 ;;
+ esac
+done
+
+
+
+[ $# -eq 0 ] && {
+ usage
+ exit 2
+}
+
+host=$1
+
+
+opts="-M $method $opts"
+opts="$opts -z $sendwait"
+[ -n "$iface" ] && opts="$opts -i $iface"
+
+[ -n "$TP_OUTPUT_STYLE" -a "$TP_OUTPUT_STYLE" != "classic" ] && {
+ echo "$prgname: warning: only classic output style supported" >&2
+}
+
+[ -n "$TP_RA_SERVER" -a -z "$RA_SERVER" ] && RA_SERVER=$TP_RA_SERVER
+
+
+[ -z "$cont" ] && exec traceroute $opts $host $length
+
+while [ "$cont" -gt 0 ]
+do
+ cont=$(($cont - 1))
+ traceroute $opts $host $length
+done
+
diff --git a/wrappers/traceroute-nanog b/wrappers/traceroute-nanog
new file mode 100755
index 0000000..0145464
--- /dev/null
+++ b/wrappers/traceroute-nanog
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Dmitry K. Butskoy
+# <dmitry@butskoy.name>
+# License: GPL v2 or any later
+#
+# See COPYING for the status of this software.
+#
+
+#
+# Shell wrapper providing traceroute-nanog(8) command line interface.
+#
+# The original implementation of traceroute-nanog(8) can be obtained
+# from ftp://ftp.login.com/pub/software/traceroute/
+#
+
+
+prgname=$0
+opts=""
+spray=""
+
+
+usage () {
+ echo "Usage: $prgname [-adnruvAMOPQU$] [-w wait] [-S start_ttl]
+ [-m max_ttl] [-p port] [-q nqueries] [-g gateway] [-t tos]
+ [-s src_addr] [-I proto] host [data_size]" >&2
+}
+
+warning () {
+ echo "$prgname: Option '$1' is not implemented in this wrapper" >&2
+}
+
+
+PARSED=`getopt -- 'adnruvAMOPQU$w:S:m:p:q:g:t:s:I:f:T:' "$@"`
+[ $? != 0 ] && {
+ usage
+ exit 2
+}
+
+eval set -- "$PARSED"
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ -[Adrn]) opts="$opts $1"; shift ;;
+ -[gmpqstw]) opts="$opts $1 $2"; shift 2 ;;
+ -I) case "$2" in
+ icmp) opts="$opts -I" ;;
+ tcp) opts="$opts -T" ;;
+ udp) ;;
+ udplite) opts="$opts -UL" ;;
+ *) opts="$opts -P $2" ;;
+ esac
+ shift 2 ;;
+ -S) opts="$opts -f $2"; shift 2 ;;
+ -P) spray=1; shift ;;
+ -f) opts="$opts --sport=$2"; shift 2 ;;
+ -u) ;;
+ -$) opts="-f 64 -m 64 -q 1"; shift ;;
+ -M) opts="$opts --mtu"; shift ;;
+ -[aUOvQ]) warning $1; shift ;;
+ -T) warning $1; shift 2 ;;
+ --) shift; break ;;
+ *) echo "$prgname: Internal parsing error" >&2; exit 2 ;;
+ esac
+done
+
+[ $# -eq 0 ] && {
+ usage
+ exit 2
+}
+
+[ -z "$spray" ] && opts="$opts -N 1"
+
+exec traceroute $opts $1 $2
+