]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
mtr v0.21 v0.21
authorMatt Kimball <mkimball@xmission.com>
Sat, 17 Oct 1998 00:00:00 +0000 (00:00 +0000)
committerTravis Cross <tc@traviscross.com>
Sun, 3 Feb 2013 20:45:27 +0000 (20:45 +0000)
v0.21
 - mtr now drops root permissions after it acquires the raw sockets it
   needs.
 - mtr should be a bit happier about building under SCO and Solaris.
 - Fixed the problem with packets arriving after a reset.

v0.20
 - The build process for mtr now uses automake.
 - Fixed a build problem for Irix.
 - Now uses non-blocking DNS code, so mtr can attempt to do reverse
   lookup on multiple hosts at once.
 - Fewer packets are sent out each cycle, so mtr doesn't hog quite so
   much bandwidth.

v0.19
 - Fixed a type-o in curses.c

v0.18
 - Fixed the network code to work properly under FreeBSD.
 - Hopefully this will fix some other operating systems too.
 - Also, fixed a build problem and the DNS hanging bug.

v0.17
 - Fixed the configure script to always like with the math library.
   Added an icon.

v0.16
 - Added one #include to select.c.  Some people were unable to build
   mtr without this line.

v0.15
 - Both the build process and the networking code have been cleaned up
   and reorganized.  mtr now builds cleanly with GTK+ 0.99.8.

2002-03-06  Cougar <cougar@random.ee>
 - If hop doesn't respond, draw its name in red (GTK) or bold (curses)

2002-02-09  Bohdan Vlasyuk <bohdan@vstu.edu.ua>
 - Added --address option to bind to given IP addess

2001-04-15  Alan Eldridge <alane@geeksrus.net>
 - Added this file so that automake won't complain.  Commented out the
   test for res_init in configure.in; it does not work for GLIBC2
   systems (e.g., RedHat 7+).
 - Fixed the subordinate CHECK_LIBS on the test for res_mkquery, so
   that they test for res_mkquery, not res_init.

source: ftp://ftp.bitwizard.nl/mtr/mtr-0.21.tar.gz

28 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
SECURITY [new file with mode: 0644]
configure.in [new file with mode: 0644]
curses.c [new file with mode: 0644]
display.c [new file with mode: 0644]
display.h [new file with mode: 0644]
dns.c [new file with mode: 0644]
dns.h [new file with mode: 0644]
getopt.c [new file with mode: 0644]
getopt.h [new file with mode: 0644]
getopt1.c [new file with mode: 0644]
gtk.c [new file with mode: 0644]
img/Makefile.am [new file with mode: 0644]
img/mtr_icon.xpm [new file with mode: 0644]
mtr-curses.h [new file with mode: 0644]
mtr-gtk.h [new file with mode: 0644]
mtr.8 [new file with mode: 0644]
mtr.c [new file with mode: 0644]
net.c [new file with mode: 0644]
net.h [new file with mode: 0644]
report.c [new file with mode: 0644]
report.h [new file with mode: 0644]
select.c [new file with mode: 0644]
select.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..c3ecbfa
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,13 @@
+  Matt Kimball <mkimball@xmission.com> is the primary author of mtr.
+
+  Bug reports and feature requests should be sent to the mtr 
+  mailing list.  See the README file for details.
+
+  Thanks to everyone who has provided feedback on mtr.  
+
+  Thanks especially to those of you who have sent me code:
+
+       Brian Casey, Mircea Damian, Christophe Kalt, Simon Kirby,
+        Anand Kumria, Charles Levert, Russell Nelson, 
+        Aaron Scarisbrick, Andrew Stesin, Juha Takala, Rogier Wolff
+        and anyone who has slipped through the cracks of my mail file.
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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) 19yy  <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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..d5648d9
--- /dev/null
@@ -0,0 +1,22 @@
+SUBDIRS = img
+
+sbin_PROGRAMS = mtr
+man_MANS = mtr.8
+install-exec-am: 
+       chmod u+s $(sbindir)/mtr
+
+mtr_SOURCES = mtr.c \
+              net.c net.h \
+              dns.c dns.h \
+              display.c display.h \
+              report.c report.h \
+              getopt.c getopt1.c getopt.h \
+              select.c select.h \
+              mtr-curses.h \
+              mtr-gtk.h
+EXTRA_mtr_SOURCES = curses.c \
+                    gtk.c
+mtr_DEPENDENCIES = $(GTK_OBJ) $(CURSES_OBJ)
+mtr_LDFLAGS = $(GTK_OBJ) $(CURSES_OBJ)
+
+EXTRA_DIST = SECURITY mtr.8
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..08886b9
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,37 @@
+WHAT'S NEW?
+
+  v0.21
+        mtr now drops root permissions after it acquires the raw
+        sockets it needs.
+       mtr should be a bit happier about building under SCO and
+        Solaris.
+       Fixed the problem with packets arriving after a reset.
+
+  v0.20
+       The build process for mtr now uses automake.
+        Fixed a build problem for Irix.
+        Now uses non-blocking DNS code, so mtr can attempt
+        to do reverse lookup on multiple hosts at once.
+       Fewer packets are sent out each cycle, so mtr
+       doesn't hog quite so much bandwidth.
+
+  v0.19
+       Fixed a type-o in curses.c
+
+  v0.18
+       Fixed the network code to work properly under FreeBSD.  
+       Hopefully this will fix some other operating systems too.
+       Also, fixed a build problem and the DNS hanging bug.
+
+  v0.17
+        Fixed the configure script to always like with the math
+       library.  Added an icon.
+
+  v0.16
+       Added one #include to select.c.  Some people were unable
+       to build mtr without this line.  
+
+  v0.15
+       Both the build process and the networking code have
+       been cleaned up and reorganized.  mtr now builds 
+       cleanly with GTK+ 0.99.8.  
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..42d7c52
--- /dev/null
+++ b/README
@@ -0,0 +1,44 @@
+WHAT IS MTR?
+
+  mtr combines the functionaly of the 'traceroute' and 'ping' programs 
+  in a single network diagnostic tool.
+
+  As mtr starts, it investigates the network connection between the host
+  mtr runs on and a user-specified destination host.  After it
+  determines the address of each network hop between the machines, 
+  it sends a sequence ICMP ECHO requests to each one to determine the 
+  quality of the link to each machine.  As it does this, it prints
+  running statistics about each machine.
+
+  mtr is distributed under the GNU General Public License. 
+  See the COPYING file for details.  
+
+INSTALLING
+
+  To begin mtr, first use the included configure script:
+
+       ./configure
+
+  The configure script will generate a Makefile.  Build mtr:
+
+       make
+
+  After compiling, install:
+
+       make install
+
+  Note that mtr must be suid-root because it requires access to raw IP 
+  sockets.  See SECURITY for security information.
+
+WHERE CAN I GET THE LATEST VERSION OR MORE INFORMATION?
+
+  See the mtr web page at 'http://www.mkimball.org/mtr.html'.
+
+  Subscribe to the mtr mailing list.  All mtr related announcements
+  are posted to the mtr mailing list.  To subscribe, send email to
+  'majordomo@lists.xmission.com' with 'subscribe mtr' in the body of
+  the message.  To send a message to the mailing list, mail to 
+  'mtr@lists.xmission.com'.
+
+  Bug reports and feature requests should be sent to the mtr 
+  mailing list.
diff --git a/SECURITY b/SECURITY
new file mode 100644 (file)
index 0000000..d81a954
--- /dev/null
+++ b/SECURITY
@@ -0,0 +1,27 @@
+SECURITY ISSUES RELATED TO MTR
+
+Since mtr is installed as suid-root, some concern over security is
+justified.  Since version 0.21 of mtr, does the following two things
+after it is launched:
+
+*  mtr requests a pair of raw sockets from the kernel.  
+*  mtr sets the effective uid to match the real uid.
+
+See main() in mtr.c and net_preopen() in net.c for the details of this
+process.  Note that no code from GTK+ or curses is executed before the
+drop in permissions.
+
+This should severely limit the possibilities of using mtr to breach
+system security.  This means the worst case scenerio is as follows:
+
+Due to some oversight in the mtr code, a malicious user is able to
+overrun one of mtr's internal buffers with binary code that is
+eventually executed.  The malicious user is still not able to read
+from or write to any system files which they wouldn't normally have
+permission to write to.  The only priveledge gained is access to the
+raw socket descriptors, which would allow the malicious user to listen
+to all ICMP packets arriving at the system, and send forged packets
+with arbitrary ncontents.
+
+If you have further questions or comments about security issues,
+please direct them to the mtr mailing list.  See README for details.
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..482bd5e
--- /dev/null
@@ -0,0 +1,48 @@
+AC_INIT(mtr.c)
+AM_INIT_AUTOMAKE(mtr, 0.21)
+
+AC_SUBST(GTK_OBJ)
+AC_SUBST(CURSES_OBJ)
+
+GTK_OBJ=gtk.o
+CURSES_OBJ=curses.o
+
+AC_PROG_CC
+
+AC_CHECK_SIZEOF(unsigned char, 1)
+AC_CHECK_SIZEOF(unsigned short, 2)
+AC_CHECK_SIZEOF(unsigned int, 4)
+AC_CHECK_SIZEOF(unsigned long, 4)
+
+AC_CHECK_FUNC(initscr, , 
+  AC_CHECK_LIB(ncurses, initscr, , 
+    AC_CHECK_LIB(curses, initscr, , 
+       AC_MSG_WARN(Building without curses display support)
+       AC_DEFINE(NO_CURSES)
+       CURSES_OBJ=)))
+AC_CHECK_HEADERS(ncurses.h ncurses/curses.h curses.h)
+AC_CHECK_HEADERS(sys/xti.h)
+
+AC_CHECK_LIB(m, floor, , AC_MSG_ERROR(No math library found))
+
+AM_PATH_GTK(1.0.0, CFLAGS="$CFLAGS $GTK_CFLAGS"
+                  LIBS="$LIBS $GTK_LIBS",
+                  AC_MSG_WARN(Building without GTK+ display support)
+                  AC_DEFINE(NO_GTK)
+                  GTK_OBJ=)
+
+AC_CHECK_FUNC(socket, , 
+  AC_CHECK_LIB(socket, socket, , AC_MSG_ERROR(No socket library found)))
+
+AC_CHECK_FUNC(gethostbyname, ,
+  AC_CHECK_LIB(nsl, gethostbyname, , AC_MSG_ERROR(No nameservice library found)))
+
+AC_CHECK_FUNC(res_mkquery, , 
+  AC_CHECK_LIB(resolv, res_mkquery, , AC_MSG_ERROR(No resolver library found)))
+
+AC_CHECK_FUNC(herror, , AC_DEFINE(NO_HERROR))
+AC_CHECK_FUNC(strerror, , AC_DEFINE(NO_STRERROR))
+
+AM_CONFIG_HEADER(config.h)
+AC_OUTPUT(Makefile img/Makefile)
+
diff --git a/curses.c b/curses.c
new file mode 100644 (file)
index 0000000..f49da44
--- /dev/null
+++ b/curses.c
@@ -0,0 +1,160 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+
+#ifndef NO_CURSES
+#include <ctype.h>
+#include <stdlib.h>
+
+#if defined(HAVE_NCURSES_H)
+#  include <ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+#  include <ncurses/curses.h>
+#elif defined(HAVE_CURSES_H)
+#  include <curses.h>
+#else
+#  error No curses header file available
+#endif
+
+#ifndef getmaxyx
+#  define getmaxyx(win,y,x)    (y = (win)->_maxy + 1, x = (win)->_maxx + 1)
+#endif
+
+#include "mtr-curses.h"
+#include "display.h"
+#include "net.h"
+#include "dns.h"
+#endif
+
+void pwcenter(char *str) {
+  int maxx, maxy;
+  int cx;
+
+  getmaxyx(stdscr, maxy, maxx);
+  cx = (signed)(maxx - strlen(str)) / 2;
+  while(cx-- > 0)
+    printw(" ");
+  printw(str);
+}
+
+int mtr_curses_keyaction() {
+  char c = getch();
+
+  if(tolower(c) == 'q')
+    return ActionQuit;
+  if(c==3)
+     return ActionQuit;
+  if(tolower(c) == 'r')
+    return ActionReset;
+
+  return 0;
+}
+
+void mtr_curses_hosts(int startstat) {
+  int max;
+  int at;
+  int addr;
+  int y, x;
+  char *name;
+
+  max = net_max();
+
+  for(at = 0; at < max; at++) {
+    printw("%2d. ", at + 1);
+    addr = net_addr(at);
+
+    if(addr != 0) {
+      name = dns_lookup(addr);
+      if(name != NULL) {
+       printw("%s", name);
+      } else {
+       printw("%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, 
+              (addr >> 8) & 0xff, addr & 0xff);
+      }
+
+      getyx(stdscr, y, x);
+      move(y, startstat);
+
+      printw("  %3d%% %4d%4d %5d%5d%7d", 
+             net_percent(at),
+             net_returned(at), net_xmit(at),
+             net_best(at), net_avg(at), net_worst(at));
+
+
+    } else {
+      printw("???");
+    }
+
+    printw("\n");
+  }
+}
+
+void mtr_curses_redraw() {
+  int maxx, maxy;
+  int startstat;
+  int rowstat;
+
+  erase();
+  getmaxyx(stdscr, maxy, maxx);
+
+  /* Modified by Brian Casey December 1997 bcasey@imagiware.com */
+  startstat = maxx - 40;
+
+  rowstat = 5;
+
+  attron(A_BOLD);
+  move(0, 0);
+  pwcenter("Matt's traceroute  [v" VERSION "]");
+  printw("\n\n");
+  attroff(A_BOLD);
+
+  printw("Keys:  ");
+  attron(A_BOLD);  printw("R");  attroff(A_BOLD);
+  printw(" - Restart statistics    ");
+  attron(A_BOLD);  printw("Q");  attroff(A_BOLD);
+  printw(" - Quit\n");
+  
+  attron(A_BOLD);
+  mvprintw(rowstat - 1, 0, "Hostname");
+
+  /* Modified by Brian Casey December 1997 bcasey@imagiware.com */
+  mvprintw(rowstat - 2, startstat, "    Packets            Pings");
+  mvprintw(rowstat - 1, startstat, " %%Loss  Rcv Snt  Best  Avg  Worst");
+
+  attroff(A_BOLD);
+  move(rowstat, 0);
+
+  mtr_curses_hosts(startstat);
+
+  refresh();
+}
+
+void mtr_curses_open() {
+  initscr();
+  raw();
+  noecho(); 
+
+  mtr_curses_redraw();
+}
+
+void mtr_curses_close() {  
+  printw("\n");
+  endwin();
+}
diff --git a/display.c b/display.c
new file mode 100644 (file)
index 0000000..f140aa9
--- /dev/null
+++ b/display.c
@@ -0,0 +1,128 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+#include "display.h"
+#include "mtr-curses.h"
+#include "mtr-gtk.h"
+#include "report.h"
+#include "select.h"
+
+extern int DisplayMode;
+
+void display_detect(int *argc, char ***argv) {
+  DisplayMode = DisplayReport;
+
+#ifndef NO_CURSES
+  DisplayMode = DisplayCurses;
+#endif
+
+#ifndef NO_GTK
+  if(gtk_detect(argc, argv)) {
+    DisplayMode = DisplayGTK;
+  }
+#endif
+}
+
+void display_open() {
+  switch(DisplayMode) {
+  case DisplayReport:
+    report_open();
+    break;
+
+#ifndef NO_CURSES
+  case DisplayCurses:
+    mtr_curses_open();  
+    break;
+#endif
+
+#ifndef NO_GTK
+  case DisplayGTK:
+    gtk_open();
+    break;
+#endif
+  }
+}
+
+void display_close() {
+  switch(DisplayMode) {
+  case DisplayReport:
+    report_close();
+    break;
+
+#ifndef NO_CURSES
+  case DisplayCurses:
+    mtr_curses_close();
+    break;
+#endif
+
+#ifndef NO_GTK
+  case DisplayGTK:
+    gtk_close();
+    break;
+#endif
+  }
+}
+
+void display_redraw() {
+  switch(DisplayMode) {
+#ifndef NO_CURSES
+  case DisplayCurses:
+    mtr_curses_redraw();
+    break;
+#endif
+
+#ifndef NO_GTK
+  case DisplayGTK:
+    gtk_redraw();
+    break;
+#endif
+  }
+}
+
+int display_keyaction() {
+  switch(DisplayMode) {
+#ifndef NO_CURSES
+  case DisplayCurses:
+    return mtr_curses_keyaction();
+#endif
+
+#ifndef NO_GTK
+  case DisplayGTK:
+    return gtk_keyaction();
+#endif
+  }
+  return 0;
+}
+
+void display_loop() {
+  switch(DisplayMode) {
+  case DisplayCurses:
+  case DisplayReport:
+    select_loop();
+    break;
+
+#ifndef NO_GTK
+  case DisplayGTK:
+    gtk_loop();
+    break;
+#endif
+  }
+}
+
diff --git a/display.h b/display.h
new file mode 100644 (file)
index 0000000..9d89d56
--- /dev/null
+++ b/display.h
@@ -0,0 +1,29 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+enum { ActionNone, ActionQuit, ActionReset };
+enum { DisplayReport, DisplayCurses, DisplayGTK };
+
+/*  Prototypes for display.c  */
+void display_detect(int *argc, char ***argv);
+void display_open();
+void display_close();
+void display_redraw();
+int display_keyaction();
+void display_loop();
diff --git a/dns.c b/dns.c
new file mode 100644 (file)
index 0000000..42c1c75
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,1157 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    Non-blocking DNS portion --
+    Copyright (C) 1998 by Simon Kirby <sim@neato.org>
+    Released under GPL, as above.
+*/
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef NO_STRERROR
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define strerror(errno) (((errno) >= 0 && (errno) < sys_nerr) ? sys_errlist[errno] : "unlisted error")
+#endif
+
+/*  Hmm, it seems Irix requires this  */
+extern int errno;
+
+/* Defines */
+
+#undef Debug
+
+#undef CorruptCheck
+#undef WipeFrees
+#undef WipeMallocs
+
+#define BashSize 8192                  /* Size of hash tables */
+#define BashModulo(x) ((x) & 8191)     /* Modulo for hash table size: */
+#define HostnameLength 255             /* From RFC */
+#define ResRetryDelay1 3
+#define ResRetryDelay2 4
+#define ResRetryDelay3 5
+
+/* Macros */
+
+#define nonull(s) (s) ? s : nullstring
+
+/* Typedefs */
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long dword;
+
+typedef unsigned int ip_t;
+
+/* Structures */
+
+struct resolve {
+   struct resolve *next;
+   struct resolve *previous;
+   struct resolve *nextid;
+   struct resolve *previousid;
+   struct resolve *nextip;
+   struct resolve *previousip;
+   struct resolve *nexthost;
+   struct resolve *previoushost;
+   char *hostname;
+   double expiretime;
+   ip_t ip;
+   word id;
+   byte state;
+};
+
+/* Non-blocking nameserver interface routines */
+
+#define MaxPacketsize (PACKETSZ)
+#define DomainLength (MAXDNAME)
+
+#define OpcodeCount 3
+char *opcodes[OpcodeCount+1] = {
+   "standard query",
+   "inverse query",
+   "server status request",
+   "unknown",
+};
+
+#define ResponsecodeCount 6
+char *responsecodes[ResponsecodeCount+1] = {
+   "no error",
+   "format error in query",
+   "server failure",
+   "queried domain name does not exist",
+   "requested query type not implemented",
+   "refused by name server",
+   "unknown error",
+};
+
+#define ResourcetypeCount 17
+char *resourcetypes[ResourcetypeCount+1] = {
+   "unknown type",
+   "A: host address",
+   "NS: authoritative name server",
+   "MD: mail destination (OBSOLETE)",
+   "MF: mail forwarder (OBSOLETE)",
+   "CNAME: name alias",
+   "SOA: authority record",
+   "MB: mailbox domain name (EXPERIMENTAL)",
+   "MG: mail group member (EXPERIMENTAL)",
+   "MR: mail rename domain name (EXPERIMENTAL)",
+   "NULL: NULL RR (EXPERIMENTAL)",
+   "WKS: well known service description",
+   "PTR: domain name pointer",
+   "HINFO: host information",
+   "MINFO: mailbox or mail list information",
+   "MX: mail exchange",
+   "TXT: text string",
+   "unknown type",
+};
+
+#define ClasstypeCount 5
+char *classtypes[ClasstypeCount+1] = {
+   "unknown class",
+   "IN: the Internet",
+   "CS: CSNET (OBSOLETE)",
+   "CH: CHAOS",
+   "HS: Hesoid [Dyer 87]",
+   "unknown class"
+};
+
+char *rrtypes[] = {
+   "Unknown",
+   "Query",
+   "Answer",
+   "Authority reference",
+   "Resource reference",
+};
+
+enum {
+   RR_UNKNOWN,
+   RR_QUERY,
+   RR_ANSWER,
+   RR_AUTHORITY,
+   RR_RESOURCE,
+};
+
+typedef struct {
+   word id;             /* Packet id */
+   byte databyte_a;
+      /* rd:1           recursion desired
+       * tc:1           truncated message
+       * aa:1           authoritive answer
+       * opcode:4       purpose of message
+       * qr:1           response flag
+       */
+   byte databyte_b;
+      /* rcode:4        response code
+       * unassigned:2   unassigned bits
+       * pr:1           primary server required (non standard)
+       * ra:1           recursion available
+       */
+   word qdcount;        /* Query record count */
+   word ancount;        /* Answer record count */
+   word nscount;        /* Authority reference record count */
+   word arcount;        /* Resource reference record count */
+} packetheader;
+
+#ifndef HFIXEDSZ
+#define HFIXEDSZ (sizeof(packetheader))
+#endif
+
+/*
+ * Byte order independent macros for packetheader
+ */
+#define getheader_rd(x) (x->databyte_a & 1)
+#define getheader_tc(x) ((x->databyte_a >> 1) & 1)
+#define getheader_aa(x) ((x->databyte_a >> 2) & 1)
+#define getheader_opcode(x) ((x->databyte_a >> 3) & 15)
+#define getheader_qr(x) (x->databyte_a >> 7)
+#define getheader_rcode(x) (x->databyte_b & 15)
+#define getheader_pr(x) ((x->databyte_b >> 6) & 1)
+#define getheader_ra(x) (x->databyte_b >> 7)
+
+#define sucknetword(x) (((word)*(x) << 8) | (((x)+= 2)[-1]))
+#define sucknetshort(x) (((short)*(x) << 8) | (((x)+= 2)[-1]))
+#define sucknetdword(x) (((dword)*(x) << 24) | ((x)[1] << 16) | ((x)[2] << 8) | (((x)+= 4)[-1]))
+#define sucknetlong(x) (((long)*(x) << 24) | ((x)[1] << 16) | ((x)[2] << 8) | (((x)+= 4)[-1]))
+
+enum {
+   STATE_FINISHED,
+   STATE_FAILED,
+   STATE_PTRREQ1,
+   STATE_PTRREQ2,
+   STATE_PTRREQ3,
+};
+
+#define Is_PTR(x) ((x->state == STATE_PTRREQ1) || (x->state == STATE_PTRREQ2) || (x->state == STATE_PTRREQ3))
+
+dword resrecvbuf[(MaxPacketsize + 7) >> 2]; /* MUST BE DWORD ALIGNED */
+
+struct resolve *idbash[BashSize];
+struct resolve *ipbash[BashSize];
+struct resolve *hostbash[BashSize];
+struct resolve *expireresolves = NULL;
+struct resolve *lastresolve = NULL;
+struct logline *streamlog = NULL;
+struct logline *lastlog = NULL;
+
+ip_t alignedip;
+ip_t localhost;
+
+double sweeptime;
+
+#ifdef Debug
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+dword mem = 0;
+
+dword res_iplookupsuccess = 0;
+dword res_reversesuccess = 0;
+dword res_nxdomain = 0;
+dword res_nserror = 0;
+dword res_hostipmismatch = 0;
+dword res_unknownid = 0;
+dword res_resend = 0;
+dword res_timeout = 0;
+
+dword resolvecount = 0;
+
+long idseed = 0xdeadbeef;
+long aseed;
+
+struct sockaddr_in from;
+
+int resfd;
+int fromlen = sizeof(struct sockaddr_in);
+
+char tempstring[16384+1+1];
+char sendstring[1024+1];
+char namestring[1024+1];
+char stackstring[1024+1];
+
+char nullstring[] = "";
+
+/* Code */
+
+void *statmalloc(size_t size){
+   void *p;
+   size_t mallocsize;
+   mem+= size;
+#ifdef CorruptCheck
+   mallocsize = size + (sizeof(dword)) + sizeof(dword);
+#else
+   mallocsize = size + (sizeof(dword));
+#endif
+   p = malloc(mallocsize);
+   if (!p){
+      fprintf(stderr,"malloc() of %u bytes failed: %s\n",size,strerror(errno));
+      exit(-1);
+   }
+   *((dword *)p) = (dword)size;
+#ifdef CorruptCheck
+   *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 0) = 0xde;
+   *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 1) = 0xad;
+   *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 2) = 0xbe;
+   *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 3) = 0xef;
+#endif
+   p = (void *)((dword *)p + 1);
+#ifdef WipeMallocs
+   memset(p,0xf0,size);
+#endif
+   return p;
+}
+
+void statfree(void *p){
+   if (p){
+      if (*((dword *)p - 1) == 0){
+         fprintf(stderr,"ERROR: Attempt to free pointer twice.\n");
+         *(int*)0=0;
+         exit(-1);
+      } else {
+         if (*((dword *)p - 1) > 8192){
+            fprintf(stderr,"ERROR: Corrupted free() buffer. (header)\n");
+            *(int*)0=0;
+            exit(-1);
+         }
+#ifdef CorruptCheck
+         if ((*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 0) != 0xde) ||
+              (*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 1) != 0xad) ||
+               (*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 2) != 0xbe) ||
+                (*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 3) != 0xef)){
+            fprintf(stderr,"ERROR: Corrupted free() buffer. (footer)\n");
+            *(int*)0=0;
+            exit(-1);
+         }
+#endif
+         mem-= *((dword *)p - 1);
+#ifdef WipeFrees
+         memset(p,0xfe,*((dword *)p - 1));
+         *((dword *)p - 1) = 0;
+#endif
+         free((dword *)p - 1);
+      }
+   }
+}
+
+char *strtdiff(char *d,long signeddiff){
+   dword diff;
+   dword seconds,minutes,hours;
+   long days;
+   if ((diff = labs(signeddiff))){
+      seconds = diff % 60; diff/= 60;
+      minutes = diff % 60; diff/= 60;
+      hours = diff % 24;
+      days = signeddiff / (60 * 60 * 24);
+      if (days)
+         sprintf(d,"%lid",days);
+      else
+         *d = '\0';
+      if (hours)
+         sprintf(d + strlen(d),"%luh",hours);
+      if (minutes)
+         sprintf(d + strlen(d),"%lum",minutes);
+      if (seconds)
+         sprintf(d + strlen(d),"%lus",seconds);
+   } else
+      sprintf(d,"0s");
+   return d;
+}
+
+int issetfd(fd_set *set,int fd){
+   return (int)((FD_ISSET(fd,set)) && 1);
+}
+
+void setfd(fd_set *set,int fd){
+   FD_SET(fd,set);
+}
+
+void clearfd(fd_set *set,int fd){
+   FD_CLR(fd,set);
+}
+
+void clearset(fd_set *set){
+   FD_ZERO(set);
+}
+
+char *strlongip(ip_t ip){
+   struct in_addr a;
+   a.s_addr = ip;
+   return inet_ntoa(a);
+}
+
+ip_t longipstr(char *s){
+   return inet_addr(s);
+}
+
+int dns_forward(char *name){
+   struct hostent *host;
+   if ((host = gethostbyname(name)))
+      return *(int *)host->h_addr;
+   else
+      return 0;
+}
+
+int dns_waitfd(){
+   return resfd;
+}
+
+void dns_open(){
+   int option,i;
+   res_init();
+   if (!_res.nscount){
+      fprintf(stderr,"No nameservers defined.\n");
+      exit(-1);
+   }
+   _res.options|= RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
+   for (i = 0;i < _res.nscount;i++)
+      _res.nsaddr_list[i].sin_family = AF_INET;
+   resfd = socket(AF_INET, SOCK_DGRAM, 0);
+   if (resfd == -1){
+      fprintf(stderr,"Unable to allocate socket for nameserver communication: %s\n",
+       strerror(errno));
+      exit(-1);
+   }
+   option = 1;
+   if (setsockopt(resfd,SOL_SOCKET,SO_BROADCAST,(char *)&option,sizeof(option))){
+      fprintf(stderr,"Unable to setsockopt() on nameserver communication socket: %s\n",
+       strerror(errno));
+      exit(-1);
+   }
+   localhost = longipstr("127.0.0.1");
+   aseed = time(NULL) ^ (time(NULL) << 3) ^ (dword)getpid();
+   for (i = 0;i < BashSize;i++){
+      idbash[i] = NULL;
+      hostbash[i] = NULL;
+   }
+}
+
+struct resolve *allocresolve(){
+   struct resolve *rp;
+   rp = (struct resolve *)statmalloc(sizeof(struct resolve));
+   if (!rp){
+      fprintf(stderr,"statmalloc() failed: %s\n",strerror(errno));
+      exit(-1);
+   }
+   bzero(rp,sizeof(struct resolve));
+   return rp;
+}
+
+dword getidbash(word id){
+   return (dword)BashModulo(id);
+}
+
+dword getipbash(ip_t ip){
+   return (dword)BashModulo(ip);
+}
+
+dword gethostbash(char *host){
+   dword bashvalue = 0;
+   for (;*host;host++){
+      bashvalue^= *host;
+      bashvalue+= (*host >> 1) + (bashvalue >> 1);
+   }
+   return BashModulo(bashvalue);
+}
+
+void linkresolveid(struct resolve *addrp){
+   struct resolve *rp;
+   dword bashnum;
+   bashnum = getidbash(addrp->id);
+   rp = idbash[bashnum];
+   if (rp){
+      while ((rp->nextid) && (addrp->id > rp->nextid->id))
+         rp = rp->nextid;
+      while ((rp->previousid) && (addrp->id < rp->previousid->id))
+         rp = rp->previousid;
+      if (rp->id < addrp->id){
+         addrp->previousid = rp;
+         addrp->nextid = rp->nextid;
+         if (rp->nextid)
+            rp->nextid->previousid = addrp;
+         rp->nextid = addrp;
+      } else {
+         addrp->previousid = rp->previousid;
+         addrp->nextid = rp;
+         if (rp->previousid)
+            rp->previousid->nextid = addrp;
+         rp->previousid = addrp;
+      }
+   } else
+      addrp->nextid = addrp->previousid = NULL;
+   idbash[bashnum] = addrp;
+}
+
+void unlinkresolveid(struct resolve *rp){
+   dword bashnum;
+   bashnum = getidbash(rp->id);
+   if (idbash[bashnum] == rp)
+      if (rp->previousid)
+         idbash[bashnum] = rp->previousid;
+      else
+         idbash[bashnum] = rp->nextid;
+   if (rp->nextid)
+      rp->nextid->previousid = rp->previousid;
+   if (rp->previousid)
+      rp->previousid->nextid = rp->nextid;
+}
+
+void linkresolvehost(struct resolve *addrp){
+   struct resolve *rp;
+   dword bashnum;
+   bashnum = gethostbash(addrp->hostname);
+   rp = hostbash[bashnum];
+   if (rp){
+      while ((rp->nexthost) && (strcasecmp(addrp->hostname,rp->nexthost->hostname) < 0))
+         rp = rp->nexthost;
+      while ((rp->previoushost) && (strcasecmp(addrp->hostname,rp->previoushost->hostname) > 0))
+         rp = rp->previoushost;
+      if (strcasecmp(addrp->hostname,rp->hostname) < 0){
+         addrp->previoushost = rp;
+         addrp->nexthost = rp->nexthost;
+         if (rp->nexthost)
+            rp->nexthost->previoushost = addrp;
+         rp->nexthost = addrp;
+      } else {
+         addrp->previoushost = rp->previoushost;
+         addrp->nexthost = rp;
+         if (rp->previoushost)
+            rp->previoushost->nexthost = addrp;
+         rp->previoushost = addrp;
+      }
+   } else
+      addrp->nexthost = addrp->previoushost = NULL;
+   hostbash[bashnum] = addrp;
+}
+
+void unlinkresolvehost(struct resolve *rp){
+   dword bashnum;
+   bashnum = gethostbash(rp->hostname);
+   if (hostbash[bashnum] == rp)
+      if (rp->previoushost)
+         hostbash[bashnum] = rp->previoushost;
+      else
+         hostbash[bashnum] = rp->nexthost;
+   if (rp->nexthost)
+      rp->nexthost->previoushost = rp->previoushost;
+   if (rp->previoushost)
+      rp->previoushost->nexthost = rp->nexthost;
+   statfree(rp->hostname);
+}
+
+void linkresolveip(struct resolve *addrp){
+   struct resolve *rp;
+   dword bashnum;
+   bashnum = getipbash(addrp->ip);
+   rp = ipbash[bashnum];
+   if (rp){
+      while ((rp->nextip) && (addrp->ip > rp->nextip->ip))
+         rp = rp->nextip;
+      while ((rp->previousip) && (addrp->ip < rp->previousip->ip))
+         rp = rp->previousip;
+      if (rp->ip < addrp->ip){
+         addrp->previousip = rp;
+         addrp->nextip = rp->nextip;
+         if (rp->nextip)
+            rp->nextip->previousip = addrp;
+         rp->nextip = addrp;
+      } else {
+         addrp->previousip = rp->previousip;
+         addrp->nextip = rp;
+         if (rp->previousip)
+            rp->previousip->nextip = addrp;
+         rp->previousip = addrp;
+      }
+   } else
+      addrp->nextip = addrp->previousip = NULL;
+   ipbash[bashnum] = addrp;
+}
+
+void unlinkresolveip(struct resolve *rp){
+   dword bashnum;
+   bashnum = getipbash(rp->ip);
+   if (ipbash[bashnum] == rp)
+      if (rp->previousip)
+         ipbash[bashnum] = rp->previousip;
+      else
+         ipbash[bashnum] = rp->nextip;
+   if (rp->nextip)
+      rp->nextip->previousip = rp->previousip;
+   if (rp->previousip)
+      rp->previousip->nextip = rp->nextip;
+}
+
+void linkresolve(struct resolve *rp){
+   struct resolve *irp;
+   if (expireresolves){
+      irp = expireresolves;
+      while ((irp->next) && (rp->expiretime >= irp->expiretime)) irp = irp->next;
+      if (rp->expiretime >= irp->expiretime){
+         rp->next = NULL;
+         rp->previous = irp;
+         irp->next = rp;
+         lastresolve = rp;
+      } else {
+         rp->previous = irp->previous;
+         rp->next = irp;
+         if (irp->previous)
+            irp->previous->next = rp;
+         else
+            expireresolves = rp;
+         irp->previous = rp;
+      }
+   } else {
+      rp->next = NULL;
+      rp->previous = NULL;
+      expireresolves = lastresolve = rp;
+   }
+   resolvecount++;
+}
+
+void lastlinkresolve(struct resolve *rp){
+   struct resolve *irp;
+   if (lastresolve){
+      irp = lastresolve;
+      while ((irp->previous) && (rp->expiretime < irp->expiretime)) irp = irp->previous;
+      while ((irp->next) && (rp->expiretime >= irp->expiretime)) irp = irp->next;
+      if (rp->expiretime >= irp->expiretime){
+         rp->next = NULL;
+         rp->previous = irp;
+         irp->next = rp;
+         lastresolve = rp;
+      } else {
+         rp->previous = irp->previous;
+         rp->next = irp;
+         if (irp->previous)
+            irp->previous->next = rp;
+         else
+            expireresolves = rp;
+         irp->previous = rp;
+      }
+   } else {
+      rp->next = NULL;
+      rp->previous = NULL;
+      expireresolves = lastresolve = rp;
+   }
+   resolvecount++;
+}
+
+void untieresolve(struct resolve *rp){
+   if (rp->previous)
+      rp->previous->next = rp->next;
+   else
+      expireresolves = rp->next;
+   if (rp->next)
+      rp->next->previous = rp->previous;
+   else
+      lastresolve = rp->previous;
+   resolvecount--;
+}
+
+void unlinkresolve(struct resolve *rp){
+   untieresolve(rp);
+   unlinkresolveid(rp);
+   unlinkresolveip(rp);
+   if (rp->hostname)
+      unlinkresolvehost(rp);
+}
+
+struct resolve *findid(word id){
+   struct resolve *rp;
+   int bashnum;
+   bashnum = getidbash(id);
+   rp = idbash[bashnum];
+   if (rp){
+      while ((rp->nextid) && (id >= rp->nextid->id))
+         rp = rp->nextid;
+      while ((rp->previousid) && (id <= rp->previousid->id))
+         rp = rp->previousid;
+      if (id == rp->id){
+         idbash[bashnum] = rp;
+         return rp;
+      } else
+         return NULL;
+   }
+   return rp; /* NULL */
+}
+
+struct resolve *findhost(char *hostname){
+   struct resolve *rp;
+   int bashnum;
+   bashnum = gethostbash(hostname);
+   rp = hostbash[bashnum];
+   if (rp){
+      while ((rp->nexthost) && (strcasecmp(hostname,rp->nexthost->hostname) >= 0))
+         rp = rp->nexthost;
+      while ((rp->previoushost) && (strcasecmp(hostname,rp->nexthost->hostname) <= 0))
+         rp = rp->previoushost;
+      if (strcasecmp(hostname,rp->hostname))
+         return NULL;
+      else {
+         hostbash[bashnum] = rp;
+         return rp;
+      }
+   }
+   return rp; /* NULL */
+}
+
+struct resolve *findip(ip_t ip){
+   struct resolve *rp;
+   dword bashnum;
+   bashnum = getipbash(ip);
+   rp = ipbash[bashnum];
+   if (rp){
+      while ((rp->nextip) && (ip >= rp->nextip->ip))
+         rp = rp->nextip;
+      while ((rp->previousip) && (ip <= rp->previousip->ip))
+         rp = rp->previousip;
+      if (ip == rp->ip){
+         ipbash[bashnum] = rp;
+         return rp;
+      } else
+         return NULL;
+   }
+   return rp; /* NULL */
+}
+
+void restell(char *s){
+   fputs(s,stderr);
+   fputs("\r",stderr);
+}
+
+void dorequest(char *s,int type,word id){
+   packetheader *hp;
+   int r,i;
+   byte buf[MaxPacketsize+1];
+   r = res_mkquery(QUERY,s,C_IN,type,NULL,0,NULL,buf,MaxPacketsize);
+   if (r == -1){
+      restell("Resolver error: Query too large.");
+      return;
+   }
+   hp = (packetheader *)buf;
+   hp->id = id;        /* htons() deliberately left out (redundant) */
+   for (i = 0;i < _res.nscount;i++)
+      (void)sendto(resfd,buf,r,0,(struct sockaddr *)&_res.nsaddr_list[i],
+       sizeof(struct sockaddr));
+}
+
+void resendrequest(struct resolve *rp,int type){
+   if (type == T_A){
+      dorequest(rp->hostname,type,rp->id);
+      if (debug){
+         sprintf(tempstring,"Resolver: Sent reverse authentication request for \"%s\".",
+          rp->hostname);
+         restell(tempstring);
+      }
+   } else if (type == T_PTR){
+      sprintf(tempstring,"%u.%u.%u.%u.in-addr.arpa",
+       ((byte *)&rp->ip)[3],
+        ((byte *)&rp->ip)[2],
+         ((byte *)&rp->ip)[1],
+          ((byte *)&rp->ip)[0]);
+      dorequest(tempstring,type,rp->id);
+      if (debug){
+         sprintf(tempstring,"Resolver: Sent domain lookup request for \"%s\".",
+          strlongip(rp->ip));
+         restell(tempstring);
+      }
+   }
+}
+
+void sendrequest(struct resolve *rp,int type){
+   do {
+      idseed = (((idseed + idseed) | (long)time(NULL)) + idseed - 0x54bad4a) ^ aseed;
+      aseed^= idseed;
+      rp->id = (word)idseed;
+   } while (findid(rp->id));
+   linkresolveid(rp);
+   resendrequest(rp,type);
+}
+
+void failrp(struct resolve *rp){
+   if (rp->state == STATE_FINISHED)
+      return;
+   rp->state = STATE_FAILED;
+   untieresolve(rp);
+   if (debug)
+      restell("Resolver: Lookup failed.\n");
+}
+
+void passrp(struct resolve *rp,long ttl){
+   rp->state = STATE_FINISHED;
+   rp->expiretime = sweeptime + (double)ttl;
+   untieresolve(rp);
+   if (debug){
+      sprintf(tempstring,"Resolver: Lookup successful: %s\n",rp->hostname);
+      restell(tempstring);
+   }
+}
+
+void parserespacket(byte *s,int l){
+   struct resolve *rp;
+   packetheader *hp;
+   byte *eob;
+   byte *c;
+   long ttl;
+   int r,usefulanswer;
+   word rr,datatype,class,qdatatype,qclass;
+   byte rdatalength;
+   if (l < sizeof(packetheader)){
+      restell("Resolver error: Packet smaller than standard header size.");
+      return;
+   }
+   if (l == sizeof(packetheader)){
+      restell("Resolver error: Packet has empty body.");
+      return;
+   }
+   hp = (packetheader *)s;
+   /* Convert data to host byte order */
+   /* hp->id does not need to be redundantly byte-order flipped, it is only echoed by nameserver */
+   rp = findid(hp->id);
+   if (!rp){
+      res_unknownid++;
+      return;
+   }
+   if ((rp->state == STATE_FINISHED) || (rp->state == STATE_FAILED))
+      return;
+   hp->qdcount = ntohs(hp->qdcount);
+   hp->ancount = ntohs(hp->ancount);
+   hp->nscount = ntohs(hp->nscount);
+   hp->arcount = ntohs(hp->arcount);
+   if (getheader_tc(hp)){ /* Packet truncated */
+      restell("Resolver error: Nameserver packet truncated.");
+      return;
+   }
+   if (!getheader_qr(hp)){ /* Not a reply */
+      restell("Resolver error: Query packet received on nameserver communication socket.");
+      return;
+   }
+   if (getheader_opcode(hp)){ /* Not opcode 0 (standard query) */
+      restell("Resolver error: Invalid opcode in response packet.");
+      return;
+   }
+   eob = s + l;
+   c = s + HFIXEDSZ;
+   switch (getheader_rcode(hp)){
+      case NOERROR:
+         if (hp->ancount){
+            if (debug){
+               sprintf(tempstring,"Resolver: Received nameserver reply. (qd:%u an:%u ns:%u ar:%u)",
+                hp->qdcount,hp->ancount,hp->nscount,hp->arcount);
+               restell(tempstring);
+            }
+            if (hp->qdcount != 1){
+               restell("Resolver error: Reply does not contain one query.");
+               return;
+            }
+            if (c > eob){
+               restell("Resolver error: Reply too short.");
+               return;
+            }
+            switch (rp->state){ /* Construct expected query reply */
+               case STATE_PTRREQ1:
+               case STATE_PTRREQ2:
+               case STATE_PTRREQ3:
+                  sprintf(stackstring,"%u.%u.%u.%u.in-addr.arpa",
+                   ((byte *)&rp->ip)[3],
+                    ((byte *)&rp->ip)[2],
+                     ((byte *)&rp->ip)[1],
+                      ((byte *)&rp->ip)[0]);
+                  break;
+            }
+            *namestring = '\0';
+            r = dn_expand(s,s + l,c,namestring,MAXDNAME);
+            if (r == -1){
+               restell("Resolver error: dn_expand() failed while expanding query domain.");
+               return;
+            }
+            namestring[strlen(stackstring)] = '\0';
+            if (strcasecmp(stackstring,namestring)){
+               if (debug){
+                  sprintf(tempstring,"Resolver: Unknown query packet dropped. (\"%s\" does not match \"%s\")",
+                   stackstring,namestring);
+                  restell(tempstring);
+               }
+               return;
+            }
+            if (debug){
+               sprintf(tempstring,"Resolver: Queried domain name: \"%s\"",namestring);
+               restell(tempstring);
+            }
+            c+= r;
+            if (c + 4 > eob){
+               restell("Resolver error: Query resource record truncated.");
+               return;
+            }
+            qdatatype = sucknetword(c);
+            qclass = sucknetword(c);
+            if (qclass != C_IN){
+               sprintf(tempstring,"Resolver error: Received unsupported query class: %u (%s)",
+                qclass,qclass < ClasstypeCount ? classtypes[qclass] :
+                 classtypes[ClasstypeCount]);
+               restell(tempstring);
+            }
+            switch (qdatatype){
+               case T_PTR:
+                  if (!Is_PTR(rp))
+                     if (debug){
+                        restell("Resolver warning: Ignoring response with unexpected query type \"PTR\".");
+                        return;
+                     }
+                  break;
+               default:
+                  sprintf(tempstring,"Resolver error: Received unimplemented query type: %u (%s)",
+                   qdatatype,qdatatype < ResourcetypeCount ?
+                    resourcetypes[qdatatype] : resourcetypes[ResourcetypeCount]);
+                  restell(tempstring);
+            }
+            for (rr = hp->ancount + hp->nscount + hp->arcount;rr;rr--){
+               if (c > eob){
+                  restell("Resolver error: Packet does not contain all specified resouce records.");
+                  return;
+               }
+               *namestring = '\0';
+               r = dn_expand(s,s + l,c,namestring,MAXDNAME);
+               if (r == -1){
+                  restell("Resolver error: dn_expand() failed while expanding answer domain.");
+                  return;
+               }
+               namestring[strlen(stackstring)] = '\0';
+               if (strcasecmp(stackstring,namestring))
+                  usefulanswer = 0;
+               else
+                  usefulanswer = 1;
+               if (debug){
+                  sprintf(tempstring,"Resolver: answered domain query: \"%s\"",namestring);
+                  restell(tempstring);
+               }
+               c+= r;
+               if (c + 10 > eob){
+                  restell("Resolver error: Resource record truncated.");
+                  return;
+               }
+               datatype = sucknetword(c);
+               class = sucknetword(c);
+               ttl = sucknetlong(c);
+               rdatalength = sucknetword(c);
+               if (class != qclass){
+                  sprintf(tempstring,"query class: %u (%s)",qclass,qclass < ClasstypeCount ?
+                   classtypes[qclass] : classtypes[ClasstypeCount]);
+                  restell(tempstring);
+                  sprintf(tempstring,"rr class: %u (%s)",class,class < ClasstypeCount ?
+                   classtypes[class] : classtypes[ClasstypeCount]);
+                  restell(tempstring);
+                  restell("Resolver error: Answered class does not match queried class.");
+                  return;
+               }
+               if (!rdatalength){
+                  restell("Resolver error: Zero size rdata.");
+                  return;
+               }
+               if (c + rdatalength > eob){
+                  restell("Resolver error: Specified rdata length exceeds packet size.");
+                  return;
+               }
+               if (datatype == qdatatype){
+                  if (debug){
+                     sprintf(tempstring,"Resolver: TTL: %s",strtdiff(sendstring,ttl));
+                     restell(tempstring);
+                  }
+                  if (usefulanswer)
+                     switch (datatype){
+                        case T_A:
+                           if (rdatalength != 4){
+                              sprintf(tempstring,"Resolver error: Unsupported rdata format for \"A\" type. (%u bytes)",
+                               rdatalength);
+                              restell(tempstring);
+                              return;
+                           }
+                           if (memcmp(&rp->ip,(ip_t *)c,sizeof(ip_t))){
+                              sprintf(tempstring,"Resolver: Reverse authentication failed: %s != ",
+                               strlongip(rp->ip));
+                              memcpy(&alignedip,(ip_t *)c,sizeof(ip_t));
+                              strcat(tempstring,strlongip(alignedip));
+                              restell(tempstring);
+                              res_hostipmismatch++;
+                              failrp(rp);
+                           } else {
+                              sprintf(tempstring,"Resolver: Reverse authentication complete: %s == \"%s\".",
+                               strlongip(rp->ip),nonull(rp->hostname));
+                              restell(tempstring);
+                              res_reversesuccess++;
+                              passrp(rp,ttl);
+                              return;
+                           }
+                           break;
+                        case T_PTR:
+                           *namestring = '\0';
+                           r = dn_expand(s,s + l,c,namestring,MAXDNAME);
+                           if (r == -1){
+                              restell("Resolver error: dn_expand() failed while expanding domain in rdata.");
+                              return;
+                           }
+                           if (debug){
+                              sprintf(tempstring,"Resolver: Answered domain: \"%s\"",namestring);
+                              restell(tempstring);
+                           }
+                           if (r > HostnameLength){
+                              restell("Resolver error: Domain name too long.");
+                              failrp(rp);
+                              return;
+                           }
+                           if (!rp->hostname){
+                              rp->hostname = (char *)statmalloc(strlen(namestring) + 1);
+                              if (!rp->hostname){
+                                 fprintf(stderr,"statmalloc() error: %s\n",strerror(errno));
+                                 exit(-1);
+                              }
+                              strcpy(rp->hostname,namestring);
+                              linkresolvehost(rp);
+                              passrp(rp,ttl);
+                              res_iplookupsuccess++;
+                           }
+                           break;
+                        default:
+                           sprintf(tempstring,"Resolver error: Received unimplemented data type: %u (%s)",
+                            datatype,datatype < ResourcetypeCount ?
+                             resourcetypes[datatype] : resourcetypes[ResourcetypeCount]);
+                           restell(tempstring);
+                     }
+               } else {
+                  if (debug){
+                     sprintf(tempstring,"Resolver: Ignoring resource type %u. (%s)",
+                      datatype,datatype < ResourcetypeCount ?
+                       resourcetypes[datatype] : resourcetypes[ResourcetypeCount]);
+                     restell(tempstring);
+                  }
+               }
+               c+= rdatalength;
+            }
+         } else
+            restell("Resolver error: No error returned but no answers given.");
+         break;
+      case NXDOMAIN:
+         if (debug)
+            restell("Resolver: Host not found.");
+         res_nxdomain++;
+         failrp(rp);
+         break;
+      default:
+         sprintf(tempstring,"Resolver: Received error response %u. (%s)",
+          getheader_rcode(hp),getheader_rcode(hp) < ResponsecodeCount ?
+           responsecodes[getheader_rcode(hp)] : responsecodes[ResponsecodeCount]);
+         restell(tempstring);
+         res_nserror++;
+   }
+}
+
+void dns_ack(){
+   int r,i;
+   r = recvfrom(resfd,(byte *)resrecvbuf,MaxPacketsize,0,(struct sockaddr *)&from,&fromlen);
+   if (r > 0){
+      /* Check to see if this server is actually one we sent to */
+      if (from.sin_addr.s_addr == localhost){
+         for (i = 0;i < _res.nscount;i++)
+            if ((_res.nsaddr_list[i].sin_addr.s_addr == from.sin_addr.s_addr) ||
+                (!_res.nsaddr_list[i].sin_addr.s_addr))        /* 0.0.0.0 replies as 127.0.0.1 */
+               break;
+      } else
+         for (i = 0;i < _res.nscount;i++)
+            if (_res.nsaddr_list[i].sin_addr.s_addr == from.sin_addr.s_addr)
+               break;
+      if (i == _res.nscount){
+         sprintf(tempstring,"Resolver error: Received reply from unknown source: %s",
+          strlongip(from.sin_addr.s_addr));
+         restell(tempstring);
+      } else
+         parserespacket((byte *)resrecvbuf,r);
+   } else {
+      sprintf(tempstring,"Resolver: Socket error: %s",strerror(errno));
+      restell(tempstring);
+   }
+}
+
+int istime(double x,double *sinterval){
+   if (x)
+      if (x > sweeptime){
+         if (*sinterval > x - sweeptime)
+            *sinterval = x - sweeptime;
+      } else
+         return 1;
+   return 0;
+}
+
+void dns_events(double *sinterval){
+   struct resolve *rp,*nextrp;
+   for (rp = expireresolves;(rp) && (sweeptime >= rp->expiretime);rp = nextrp){
+      nextrp = rp->next;
+      switch (rp->state){
+         case STATE_FINISHED:  /* TTL has expired */
+         case STATE_FAILED:    /* Fake TTL has expired */
+            if (debug){
+               sprintf(tempstring,"Resolver: Cache record for \"%s\" (%s) has expired. (state: %u)  Marked for expire at: %g, time: %g.",
+                nonull(rp->hostname),strlongip(rp->ip),rp->state,rp->expiretime,sweeptime);
+               restell(tempstring);
+            }
+            unlinkresolve(rp);
+            break;
+         case STATE_PTRREQ1:   /* First T_PTR send timed out */
+            resendrequest(rp,T_PTR);
+            restell("Resolver: Send #2 for \"PTR\" query...");
+            rp->state++;
+            rp->expiretime = sweeptime + ResRetryDelay2;
+            (void)istime(rp->expiretime,sinterval);
+            res_resend++;
+            break;
+         case STATE_PTRREQ2:   /* Second T_PTR send timed out */
+            resendrequest(rp,T_PTR);
+            restell("Resolver: Send #3 for \"PTR\" query...");
+            rp->state++;
+            rp->expiretime = sweeptime + ResRetryDelay3;
+            (void)istime(rp->expiretime,sinterval);
+            res_resend++;
+            break;
+         case STATE_PTRREQ3:   /* Third T_PTR timed out */
+            restell("Resolver: \"PTR\" query timed out.");
+            failrp(rp);
+            (void)istime(rp->expiretime,sinterval);
+            res_timeout++;
+            break;
+      }
+   }
+   if (expireresolves)
+      (void)istime(expireresolves->expiretime,sinterval);
+}
+
+char *dns_lookup(ip_t ip){
+   struct resolve *rp;
+   ip = htonl(ip);
+   if ((rp = findip(ip))){
+      if ((rp->state == STATE_FINISHED) || (rp->state == STATE_FAILED)){
+         if ((rp->state == STATE_FINISHED) && (rp->hostname)){
+            if (debug){
+               sprintf(tempstring,"Resolver: Used cached record: %s == \"%s\".\n",
+                strlongip(ip),rp->hostname);
+               restell(tempstring);
+            }
+            return rp->hostname;
+         } else {
+            if (debug){
+               sprintf(tempstring,"Resolver: Used failed record: %s == ???\n",
+                strlongip(ip));
+               restell(tempstring);
+            }
+            return strlongip(ip);
+         }
+      }
+      return strlongip(ip);
+   }
+   if (debug)
+      fprintf(stderr,"Resolver: Added to new record.\n");
+   rp = allocresolve();
+   rp->state = STATE_PTRREQ1;
+   rp->expiretime = sweeptime + ResRetryDelay1;
+   rp->ip = ip;
+   linkresolve(rp);
+   rp->ip = ip;
+   linkresolveip(rp);
+   sendrequest(rp,T_PTR);
+   return strlongip(ip);
+}
diff --git a/dns.h b/dns.h
new file mode 100644 (file)
index 0000000..2f4f35b
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,27 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*  Prototypes for dns.c  */
+
+void dns_open();
+int dns_waitfd();
+void dns_ack();
+void dns_events(double *sinterval);
+char *dns_lookup(int address);
+int dns_forward(char *name);
diff --git a/getopt.c b/getopt.c
new file mode 100644 (file)
index 0000000..beb7450
--- /dev/null
+++ b/getopt.c
@@ -0,0 +1,765 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95
+       Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#endif /* GNU C library.  */
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid)      gettext (msgid)
+#else
+# define _(msgid)      (msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+\f
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define        my_index        strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+       return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+\f
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+       {
+         /* Bottom segment is the short one.  */
+         int len = middle - bottom;
+         register int i;
+
+         /* Swap it with the top part of the top segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[top - (middle - bottom) + i];
+             argv[top - (middle - bottom) + i] = tem;
+           }
+         /* Exclude the moved bottom segment from further swapping.  */
+         top -= len;
+       }
+      else
+       {
+         /* Top segment is the short one.  */
+         int len = top - middle;
+         register int i;
+
+         /* Swap it with the bottom part of the bottom segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[middle + i];
+             argv[middle + i] = tem;
+           }
+         /* Exclude the moved top segment from further swapping.  */
+         bottom += len;
+       }
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+static const char *
+_getopt_initialize (optstring)
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind = 1;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+  return optstring;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0)
+    {
+      optstring = _getopt_initialize (optstring);
+      optind = 1;              /* Don't scan ARGV[0], the program name.  */
+    }
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc
+                && (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* The special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return EOF;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
+       {
+         if (ordering == REQUIRE_ORDER)
+           return EOF;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+       /* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+        or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+       if (!strncmp (p->name, nextchar, nameend - nextchar))
+         {
+           if (nameend - nextchar == strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else
+             /* Second or later nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (opterr)
+           fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+                    argv[0], argv[optind]);
+         nextchar += strlen (nextchar);
+         optind++;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*nameend)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = nameend + 1;
+             else
+               {
+                 if (opterr)
+                  if (argv[optind - 1][1] == '-')
+                   /* --option */
+                   fprintf (stderr,
+                    _("%s: option `--%s' doesn't allow an argument\n"),
+                    argv[0], pfound->name);
+                  else
+                   /* +option or -option */
+                   fprintf (stderr,
+                    _("%s: option `%c%s' doesn't allow an argument\n"),
+                    argv[0], argv[optind - 1][0], pfound->name);
+
+                 nextchar += strlen (nextchar);
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (opterr)
+                   fprintf (stderr,
+                          _("%s: option `%s' requires an argument\n"),
+                          argv[0], argv[optind - 1]);
+                 nextchar += strlen (nextchar);
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (opterr)
+           {
+             if (argv[optind][1] == '-')
+               /* --option */
+               fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+                        argv[0], nextchar);
+             else
+               /* +option or -option */
+               fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+                        argv[0], argv[optind][0], nextchar);
+           }
+         nextchar = (char *) "";
+         optind++;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (opterr)
+         {
+           if (posixly_correct)
+             /* 1003.2 specifies the format of this message.  */
+             fprintf (stderr, _("%s: illegal option -- %c\n"),
+                      argv[0], c);
+           else
+             fprintf (stderr, _("%s: invalid option -- %c\n"),
+                      argv[0], c);
+         }
+       optopt = c;
+       return '?';
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = NULL;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (opterr)
+                 {
+                   /* 1003.2 specifies the format of this message.  */
+                   fprintf (stderr,
+                          _("%s: option requires an argument -- %c\n"),
+                          argv[0], c);
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == EOF)
+       break;
+
+      switch (c)
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/getopt.h b/getopt.h
new file mode 100644 (file)
index 0000000..4ac33b7
--- /dev/null
+++ b/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+   Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+                       const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind,
+                            int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/getopt1.c b/getopt1.c
new file mode 100644 (file)
index 0000000..4580211
--- /dev/null
+++ b/getopt1.c
@@ -0,0 +1,180 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994
+       Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+       {"add", 1, 0, 0},
+       {"append", 0, 0, 0},
+       {"delete", 1, 0, 0},
+       {"verbose", 0, 0, 0},
+       {"create", 0, 0, 0},
+       {"file", 1, 0, 0},
+       {0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+                      long_options, &option_index);
+      if (c == EOF)
+       break;
+
+      switch (c)
+       {
+       case 0:
+         printf ("option %s", long_options[option_index].name);
+         if (optarg)
+           printf (" with arg %s", optarg);
+         printf ("\n");
+         break;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case 'd':
+         printf ("option d with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/gtk.c b/gtk.c
new file mode 100644 (file)
index 0000000..8fd7fed
--- /dev/null
+++ b/gtk.c
@@ -0,0 +1,325 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+
+#ifndef NO_GTK
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "net.h"
+#include "dns.h"
+#include "mtr-gtk.h"
+
+#include "img/mtr_icon.xpm"
+#endif
+
+extern char *Hostname;
+extern float WaitTime;
+
+void gtk_do_init(int *argc, char ***argv) {
+  static int done = 0;
+
+  if(!done) {
+    gtk_init(argc, argv);
+
+    done = 1;
+  }
+}
+
+int gtk_detect(int *argc, char ***argv) {
+  if(getenv("DISPLAY") != NULL) {
+    gtk_do_init(argc, argv);
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+gint Window_destroy(GtkWidget *Window, gpointer data) {
+  gtk_main_quit();
+
+  return FALSE;
+}
+
+gint Restart_clicked(GtkWidget *Button, gpointer data) {
+  net_reset();
+  gtk_redraw();
+
+  return FALSE;
+}
+
+gint Host_activate(GtkWidget *Entry, gpointer data) {
+  int addr;
+
+  addr = dns_forward(gtk_entry_get_text(GTK_ENTRY(Entry)));
+  if(addr)
+    net_reopen(addr);
+
+  return FALSE;
+}
+
+GdkPixmap *gtk_load_pixmap(char **pixmap) {
+  return gdk_pixmap_colormap_create_from_xpm_d(NULL, 
+                                              gdk_colormap_get_system(), 
+                                              NULL, NULL, pixmap);
+}
+
+void Toolbar_fill(GtkWidget *Toolbar) {
+  GtkWidget *Button;
+  GtkWidget *Label;
+  GtkWidget *Entry;
+
+  Button = gtk_button_new_with_label("Quit");
+  gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
+  gtk_signal_connect(GTK_OBJECT(Button), "clicked",
+                    GTK_SIGNAL_FUNC(Window_destroy), NULL);
+  gtk_widget_show(Button);
+
+  Button = gtk_button_new_with_label("Restart");
+  gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
+  gtk_signal_connect(GTK_OBJECT(Button), "clicked",
+                    GTK_SIGNAL_FUNC(Restart_clicked), NULL);
+  gtk_widget_show(Button);
+
+  Label = gtk_label_new("Hostname");
+  gtk_box_pack_start(GTK_BOX(Toolbar), Label, FALSE, FALSE, 0);
+  gtk_widget_show(Label);
+
+  Entry = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(Entry), Hostname);
+  gtk_signal_connect(GTK_OBJECT(Entry), "activate",
+                    GTK_SIGNAL_FUNC(Host_activate), NULL);
+  gtk_box_pack_start(GTK_BOX(Toolbar), Entry, TRUE, TRUE, 0);
+  gtk_widget_show(Entry);
+}
+
+char *Report_Text[] = { "Hostname", "Loss", "Rcv", "Snt", "Best", "Avg", "Worst", NULL };
+int Report_Positions[] = { 10, 240, 280, 320, 360, 400, 440, 0 };
+GtkWidget *Report;
+GtkWidget *ReportBody;
+
+GtkWidget *GetRow(int index) {
+  int addr;
+  char str[256], *name;
+  GtkWidget *Row, *Label;
+
+  Row = gtk_fixed_new();
+  
+  addr = net_addr(index);
+  name = "???";
+  if(addr != 0) {
+    name = dns_lookup(addr);
+    if(!name) {
+      sprintf(str, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, 
+             (addr >> 8) & 0xff, addr & 0xff);
+      name = str;
+    }
+  }
+
+  Label = gtk_label_new(name);
+  gtk_fixed_put(GTK_FIXED(Row), Label, Report_Positions[0], 0);
+  gtk_widget_show(Label);
+
+  return Row;
+}
+
+GtkWidget *Scrollarea_create() {
+  GtkWidget *List;
+  int count;
+
+  for(count = 0; Report_Positions[count]; count++);
+
+  List = GTK_WIDGET(gtk_clist_new_with_titles(count, Report_Text));
+  gtk_clist_set_policy(GTK_CLIST(List), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+  for(count = 0; Report_Positions[count + 1]; count++) {
+    gtk_clist_set_column_width(GTK_CLIST(List), count, 
+                              Report_Positions[count + 1] - 
+                              Report_Positions[count]);
+  }
+  gtk_clist_set_column_width(GTK_CLIST(List), count, 0);
+  for(count = 1; Report_Positions[count]; count++) {
+    gtk_clist_set_column_justification(GTK_CLIST(List), count, GTK_JUSTIFY_RIGHT);
+  }
+
+  ReportBody = List;
+  return List;
+}
+
+void gtk_add_row(GtkWidget *List) {
+  int at;
+  GtkWidget *Row, *Label;
+
+  Row = gtk_fixed_new();
+  
+  for(at = 0; Report_Positions[at] != 0; at++) {
+    Label = gtk_label_new("-");
+    if(at) {
+      gtk_widget_set_usize(Label, 40, 0);
+      gtk_label_set_justify(GTK_LABEL(Label), GTK_JUSTIFY_RIGHT);
+    }
+    gtk_fixed_put(GTK_FIXED(Row), Label, Report_Positions[at], 0);
+    gtk_widget_show(Label);
+  }
+
+  gtk_box_pack_start(GTK_BOX(List), Row, FALSE, FALSE, 0);
+  gtk_widget_show(Row);
+}
+
+void gtk_set_field(GtkCList *List, int row, int ix, char *str) {
+  gtk_clist_set_text(List, row, ix, str);
+}
+
+void gtk_set_field_num(GtkCList *List, int row, int ix, char *format, int num) {
+  char str[32];
+
+  sprintf(str, format, num);
+  gtk_set_field(List, row, ix, str);
+}
+
+void gtk_update_row(GtkCList *List, int row) {
+  int addr;
+  char str[256], *name;
+
+  addr = net_addr(row);
+  name = "???";
+  if(addr != 0) {
+    name = dns_lookup(addr);
+    if(!name) {
+      sprintf(str, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, 
+             (addr >> 8) & 0xff, addr & 0xff);
+      name = str;
+    }
+  }
+  gtk_set_field(List, row, 0, name);
+
+  gtk_set_field_num(List, row, 1, "%d%%", net_percent(row));
+  gtk_set_field_num(List, row, 2, "%d", net_returned(row));  
+  gtk_set_field_num(List, row, 3, "%d", net_xmit(row));
+  
+  gtk_set_field_num(List, row, 4, "%d", net_best(row));
+  gtk_set_field_num(List, row, 5, "%d", net_avg(row));  
+  gtk_set_field_num(List, row, 6, "%d", net_worst(row));
+  
+}
+
+void gtk_redraw() {
+  int at;
+  int max = net_max();
+
+  gtk_clist_freeze(GTK_CLIST(ReportBody));
+
+  while(GTK_CLIST(ReportBody)->rows < max) {
+    gtk_clist_append(GTK_CLIST(ReportBody), Report_Text);
+  }
+
+  while(GTK_CLIST(ReportBody)->rows > max) {
+    gtk_clist_remove(GTK_CLIST(ReportBody), GTK_CLIST(ReportBody)->rows - 1);
+  }
+
+  for(at = 0; at < max; at++) {
+    gtk_update_row(GTK_CLIST(ReportBody), at);
+  }
+
+  gtk_clist_thaw(GTK_CLIST(ReportBody));
+}
+
+void Window_fill(GtkWidget *Window) {
+  GtkWidget *VBox;
+  GtkWidget *Toolbar;
+  GtkWidget *List;
+
+  gtk_window_set_title(GTK_WINDOW(Window), "Matt's traceroute  [v" VERSION "]");
+  gtk_widget_set_usize(Window, 540, 400); 
+  gtk_container_border_width(GTK_CONTAINER(Window), 10);
+  VBox = gtk_vbox_new(FALSE, 10);
+
+  Toolbar = gtk_hbox_new(FALSE, 10);
+  Toolbar_fill(Toolbar);
+  gtk_box_pack_start(GTK_BOX(VBox), Toolbar, FALSE, FALSE, 0);
+  gtk_widget_show(Toolbar);
+
+  List = Scrollarea_create();
+  gtk_box_pack_start(GTK_BOX(VBox), List, TRUE, TRUE, 0);
+  gtk_widget_show(List);
+  
+  gtk_container_add(GTK_CONTAINER(Window), VBox);
+  gtk_widget_show(VBox);
+}
+
+void gtk_open() {
+  GtkWidget *Window;
+  GdkPixmap *icon;
+
+  int argc;
+  char *args[2];
+  char **argv;
+  argc = 1;
+  argv = args;
+  argv[0] = "";
+  argv[1] = NULL;
+  gtk_do_init(&argc, &argv);
+
+  Window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+  Window_fill(Window);
+
+  gtk_signal_connect_object(GTK_OBJECT(Window), "delete_event",
+                           GTK_SIGNAL_FUNC(gtk_widget_destroy), 
+                           GTK_OBJECT(Window));
+  gtk_signal_connect(GTK_OBJECT(Window), "destroy",
+                    GTK_SIGNAL_FUNC(Window_destroy), NULL);
+
+  icon = gtk_load_pixmap(mtr_icon);
+  gtk_widget_show(Window);
+  gdk_window_set_icon(Window->window, NULL, icon, NULL);
+  gdk_window_set_icon_name(Window->window, "mtr");
+}
+
+void gtk_close() {
+}
+
+int gtk_keyaction() {
+  return 0;
+}
+
+gint gtk_ping(gpointer data) {
+  gtk_redraw();
+  net_send_batch();
+  
+  return TRUE;
+}
+
+void gtk_net_data(gpointer data, gint fd, GdkInputCondition cond) {
+  net_process_return();
+}
+
+void gtk_dns_data(gpointer data, gint fd, GdkInputCondition cond) {
+  dns_ack();
+
+  gtk_redraw();
+}
+
+void gtk_loop() {
+  gtk_timeout_add((int)(1000.0 * WaitTime), gtk_ping, NULL);
+  gdk_input_add(net_waitfd(), GDK_INPUT_READ, gtk_net_data, NULL);
+  gdk_input_add(dns_waitfd(), GDK_INPUT_READ, gtk_dns_data, NULL);
+
+  gtk_main();
+}
diff --git a/img/Makefile.am b/img/Makefile.am
new file mode 100644 (file)
index 0000000..b8679df
--- /dev/null
@@ -0,0 +1 @@
+EXTRA_DIST = mtr_icon.xpm
diff --git a/img/mtr_icon.xpm b/img/mtr_icon.xpm
new file mode 100644 (file)
index 0000000..5c809b7
--- /dev/null
@@ -0,0 +1,182 @@
+/* XPM */
+static char * mtr_icon[] = {
+"48 48 131 2",
+"      c #000000",
+".     c #020204",
+"+     c #3E7E3C",
+"@     c #62BA64",
+"#     c #4E9E4C",
+"$     c #628664",
+"%     c #6EDA6C",
+"&     c #7A9E7C",
+"*     c #468E44",
+"=     c #6ACA6C",
+"-     c #224224",
+";     c #56AE54",
+">     c #326234",
+",     c #567E54",
+"'     c #7ABA7C",
+")     c #3A723C",
+"!     c #629E64",
+"~     c #428644",
+"{     c #6E926C",
+"]     c #7AEA7C",
+"^     c #4A964C",
+"/     c #62C264",
+"(     c #52A654",
+"_     c #4A7E4C",
+":     c #82AE84",
+"<     c #366A34",
+"[     c #5AB65C",
+"}     c #4A764C",
+"|     c #6AD26C",
+"1     c #8EC68C",
+"2     c #7A9A7C",
+"3     c #7AE27C",
+"4     c #82A684",
+"5     c #568E54",
+"6     c #568654",
+"7     c #3E7A3C",
+"8     c #6AC26C",
+"9     c #3E6A3C",
+"0     c #468244",
+"a     c #56A254",
+"b     c #4E8E4C",
+"c     c #5EAE5C",
+"d     c #86C684",
+"e     c #427244",
+"f     c #62A664",
+"g     c #769A74",
+"h     c #82F284",
+"i     c #5A9A5C",
+"j     c #5AA65C",
+"k     c #428244",
+"l     c #66BE64",
+"m     c #6A8E6C",
+"n     c #76DA74",
+"o     c #7E9E7C",
+"p     c #2A522C",
+"q     c #5E825C",
+"r     c #82BE84",
+"s     c #669E64",
+"t     c #6E966C",
+"u     c #529A54",
+"v     c #62C664",
+"w     c #4A824C",
+"x     c #366E34",
+"y     c #72D674",
+"z     c #427E3C",
+"A     c #5EBE5C",
+"B     c #52A254",
+"C     c #628A64",
+"D     c #4A924C",
+"E     c #6ACE6C",
+"F     c #5AB25C",
+"G     c #326634",
+"H     c #568254",
+"I     c #7ABE7C",
+"J     c #3A763C",
+"K     c #468A44",
+"L     c #7EEE7C",
+"M     c #4E9A4C",
+"N     c #56AA54",
+"O     c #86B684",
+"P     c #62B664",
+"Q     c #162A14",
+"R     c #529E54",
+"S     c #72DA74",
+"T     c #76A274",
+"U     c #4A8E4C",
+"V     c #264A24",
+"W     c #5AAE5C",
+"X     c #5EA25C",
+"Y     c #468644",
+"Z     c #729274",
+"`     c #7EEA7C",
+" .    c #4E964C",
+"..    c #66C264",
+"+.    c #56A654",
+"@.    c #4E7E4C",
+"#.    c #8AB28C",
+"$.    c #3A6A3C",
+"%.    c #5EB65C",
+"&.    c #527A54",
+"*.    c #6ED26C",
+"=.    c #92C694",
+"-.    c #7E9A7C",
+";.    c #7AE67C",
+">.    c #86AA84",
+",.    c #5E865C",
+"'.    c #6AC66C",
+").    c #3E6E3C",
+"!.    c #5AA25C",
+"~.    c #4E924C",
+"{.    c #5EB25C",
+"].    c #467244",
+"^.    c #86F284",
+"/.    c #5AAA5C",
+"(.    c #76DE74",
+"_.    c #829E84",
+":.    c #325E34",
+"<.    c #66A264",
+"[.    c #729674",
+"}.    c #66C664",
+"|.    c #4E824C",
+"1.    c #3A6E3C",
+"2.    c #76D674",
+"3.    c #427E44",
+"4.    c #62BE64",
+"5.    c #668A64",
+"6.    c #6ECE6C",
+"7.    c #366634",
+"8.    c #5A825C",
+"9.    c #7EBE7C",
+"0.    c #3E763C",
+"G 7.G G G G G 7.G < z  . .* k 1.G G G G G G 0.K x 7.7.7.7.7.< J Y D M D ) 7.7.7.7.7.7.< 7.7.7.7.",
+"7.G 7.> > > 7.G ) D M K 7 < > > > > > > > > + * < G G G G G G 7.G x k * M K x G G G G G G G G G ",
+"G G 7.G > > < ~ M D + G > > G > > 7.7.7.7.> 7 D x 7.7.7.7.7.G 7.7.G < ) ~ D M 7 7.7.7.7.7.7.7.7.",
+"7.7.G > 7.< * M ~ x > G > G > > 7.> > > > G 7 * 1.G G G G G 7.G G 7.G G G J * M ~ < G G G G G G ",
+"> G G > x D M k < G > G > > G > > G G G > 7.7 * x 7.7.7.7.G 7.7.G 7.7.7.7.G x ~ # Y < 7.7.7.7.7.",
+"G > > x ^  .0.G G G 7.> > > > > G > > > 7.> 7 U 1.G G G G 7.G G 7.G G G G 7.G < k # K < G G G G ",
+"G G <  .^ J G G 7.G 7.> G > > > > 7.7.> 7.7.7 * x 7.7.7.G 7.7.G 7.7.7.7.G 7.G G 7.+ B ~ < 7.7.7.",
+"> < D M 7 G > > > G > G > > G > 7.> > G > > + U 1.G G G 7.G G 7.G G G G 7.G 7.7.G < k B + < G G ",
+"G k # k < 7.7.G > G > > G > > > > G x k  .R F A N R U 7 x 7.G 7.7.7.7.G 7.G G 7.G G < K # J $.8.",
+") ( K < > > G G 7.G G > > > G G + M +.B M D B W ^ ^ # ( +.^ ) G G G G 7.G 7.7.G 7.7.7.x u ! & o ",
+"^  .x > > > > G 7.7.G > > > 0. .+.M K + ) < + D ) < J k * R +.D x 7.G 7.G G 7.G G G 7.e T =.& H ",
+"+.+ < G G G 7.G > G G > G ~ ( B ~ ) G > G > 7 U < 7.7.G < J * B B 7 7.G 7.7.G 7.7.9 5.o Z s a ) ",
+"D x > > > > > G > G G < K ( D 0.> G 7.> G 7.+ * < G 7.7.G G < + # ( + G G 7.G $.,.2 [.$ ).0.( k ",
+"7 7.G G G G 7.G G > 7.K +.K x > G > > G > > 7 D < 7.G G 7.7.G 7.) M +.7 7.< &.[.2 C &.7.7.7.* B ",
+"x 7.> > > > > 7.7.G k +.* < > > > G > 7.G G 7 * < 7.7.G G 7.G G G ) M B &.[.o m &.7.G 7.G 7.) %.",
+"7.G 7.7.7.G 7.G G x B ^ ) > G > 7.> 7.> > > + D 7.G G 7.7.G 7.7.7.< w r #.{ , 9 7.7.7.G 7.G < ( ",
+"> G G > 7.G > 7.7.* ( 7 G > > > > > G 7.1.z B F D 7 < G 7.G G G $.q o : I _ < > G G 7.G 7.G 7.~ ",
+"7.7.> G > 7.7.> x +.* 7.> > G > G G < k # # +.[ B N ( k G 7.$.&.2 2 C |.B D < 7.7.7.G 7.G 7.G < ",
+"> 7.> G > G G > ~ +.+ > > > > > > < K R K + K M 3.K B W K } Z -.m &.< < Y W ) G G 7.G 7.G 7.G 7.",
+"> G G > > > 7.G # ^ ) > G > G > 7.~ # + < > 7 * < G J j d 4 { , $.G 7.G 7 F + 7.7.G 7.G 7.G 7.G ",
+"> > > G G > 7.7.N * 7.> > > > > J D Y > G 7.3.D < ).$ 4 1 ' e 7.7.G G G ) # D < G G 7.G 7.G 7.7.",
+"> 7.G > > G > x +.Y G > G > G > Y ~.x G > > 7 ~.8.-.2 5.i A 7 G G 7.7.7.< D # x 7.7.G 7.G 7.G G ",
+"< < x x < x x + ; * x x x x x ) ^  .1.) 1.) 5 r o t 6 0.K A D J ) ) ) J 7  .F k 7 7 7 7 J 0.0.x ",
+"D D * * D U U # A ( * * * K * * N W U U * K X 9.<.D K K R ..N U * * * * U ( A D K K K K U * K ) ",
+"7 z z 7 7 z 7 K {.M 7 7 7 0.0.7 # B J 7 7 7 U R 0.7 7 7 D / D 0.0.7 0.J 7  .F + 0.J 0.J 0.J 0.< ",
+"G > > > G > G < N K 7.> > G G > ~ D 1.G > > 3.U G G G G * A J G G G G 7.x D # x G 7.G 7.G 7.G G ",
+"G G G > G > G < ( * 7.> > > > > J ^ K G G > + D 7.7.7.7 N ( < 7.7.7.G G ) B * < G 7.G 7.G 7.7.G ",
+"G G > > > 7.G G #  .) G > > G > > k u 0 7.G z U G G 7 ( F + < G G G 7.7.7 W + < 7.G 7.G 7.G G 7.",
+"> 7.G 7.> > > 7.~ B + > > > G G > x ~ #  .+ ~  .7 D ; ; k G 7.7.7.G G G K W x G 7.G 7.G 7.7.G 7.",
+"7.G 7.> G 7.7.G < ( D < G > > > > > < z D # %.A +.B  .7 < K = G G 7.7.J B D < 7.G 7.G 7.G G 7.x ",
+"G G G > > > > G G K ( 7 > > > > > G > G 1.+ R N K 7 x G ` (./.7.G G < K ; J G 7.G 7.G 7.7.G 7.Y ",
+"7.G G > > G G G G x B M ) > > G G > G G > > + D G G 7.7.;.E 4.G 7.7.7 N K < 7.G 7.G 7.G G 7.< ; ",
+"1.> G G > > > ` ] %.+ L 3 (.S S +.> /.L ;.(.S y 7.G 6.3 6.= = y ;...B # ` ] @ *.;.S B 7.G G J A ",
+"z G > > G 7.7.% = '.n ~.K %.= = = y *.u  .4.= = {.7.c /.= = ..u M M B J y = E 6.!.b z G 7.< D W ",
+"M ) G > > G G y = ..7.. . +.@ '.= @ V . . 1.}.= {.. G 7.6.= B . . . . 7.y = ..> . . . . G ) [ D ",
+"[ + G G G > 7.S = a . . . . 4.E = z . . . G y = @ . . G (.6.a . . . . G y =  .. . . . . < ^ W J ",
+"N B ) G G G 7.y =  .. . . 7.}.6.'.0.. . . G (.= %.. . ~ y = B . . . 7.G *.'.0 . . . G 7.7 [ K < ",
+"+ [ D < > > G % = R . . > > }.6.}.0.. . +.+.3 = {.. . B (.E W . . G G 7.y = K . . G 7.x +.( J G ",
+"< D {.Y 7.> 7.y = u . . > > ..6.'.0.. . K U (.= %.. . + y = +.. . 7.G 7.y }.Y . . G < ^ F z 7.7.",
+"> x B ; + G > y = ( . . G > }.6.}.0.. . > G (.= {.. . G (.= {.. . G 7.G y = k . . < * [ U < G G ",
+"> G J +.N + G S '.R . . > G }.6.}.0.. . G G (.= [ . . G *.= }.J . G 7.G *.= ~ . . ~ %.^ x 7.7.7.",
+"G > G 7 +.N z y E B . . G > ..6.'.0.. . > > 3 = {.. . 7.'.= = = (.3 p 7.y '.0 . . {.# ) 7.G G G ",
+"G > G G 7 ( ; @ N ~.. . > > 4.W +.1.. . G 7.= /.B . . G > {.l 4.%.R G 7.@ /.+ . . M ) G 7.7.7.7.",
+"> 7.> > G 0.# %.^ . . . > > > > > . . > > > + D . . . 7.7.7.- - Q . . G < . . . . ) G 7.G G G G ",
+"G G 7.> 7.7.) ^ F . . < G > > > G . . 7.G > 3.D 7.. G G G 7.7.. . . . x D F . . < 7.G 7.7.7.7.7.",
+"> 7.G G > G G < ~ ( [ B + < > G > > > > > 7.+ U G 7.7.7.7.G G 7.G x K W [ R + < G G 7.G G G G G ",
+"> > > G > > > > 7.J D +.[ ; K x G 7.G 7.> 7.) z G G G G 7.7.< 0. .F F B ~ 1.G 7.7.G 7.7.7.7.7.7.",
+"G 7.7.> > G 7.G > 7.< M E | +.7 > > > > G > x ) 7.> 7.7.G G < k F % / ~ G G 7.G G 7.G G G G G G "};
diff --git a/mtr-curses.h b/mtr-curses.h
new file mode 100644 (file)
index 0000000..c94880f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*  Prototypes for curses.c  */
+void mtr_curses_open();
+void mtr_curses_close();
+void mtr_curses_redraw();
+int mtr_curses_keyaction();
diff --git a/mtr-gtk.h b/mtr-gtk.h
new file mode 100644 (file)
index 0000000..fe87bb3
--- /dev/null
+++ b/mtr-gtk.h
@@ -0,0 +1,26 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*  Prototypes for gtk.c  */
+int gtk_detect(int *argc, char ***argv);
+void gtk_open();
+void gtk_close();
+void gtk_redraw();
+int gtk_keyaction();
+void gtk_loop();
diff --git a/mtr.8 b/mtr.8
new file mode 100644 (file)
index 0000000..616cfd7
--- /dev/null
+++ b/mtr.8
@@ -0,0 +1,175 @@
+.TH MTR 8 "December 28, 1997" "mtr" "mtr"
+
+
+.SH NAME
+mtr \- a network diagnostic tool
+
+
+.SH SYNOPSIS
+.B mtr 
+[\c
+.B \-hvrctgi\c
+]
+[\c
+.B \-\-help\c
+]
+[\c
+.B \-\-version\c
+]
+[\c
+.B \-\-report\c
+]
+[\c
+.B \-\-report\-cycles\ COUNT\c
+]
+[\c
+.B \-\-curses\c
+]
+[\c
+.B \-\-gtk\c
+]
+[\c
+.B \-\-interval\ SECONDS\c
+]
+.B HOSTNAME
+
+
+.SH DESCRIPTION
+
+.B mtr 
+combines the functionaly of the 
+.B traceroute
+and 
+.B ping
+programs in a single network diagnostic tool.
+
+.PP
+As 
+.B mtr 
+starts, it investigates the network connection between the host 
+.B mtr
+runs on and 
+.BR HOSTNAME . 
+After it determines the address of each network hop between the
+machines, it sends a sequence ICMP ECHO requests to each one to
+determine the quality of the link to each machine.  As it does this,
+it prints running statistics about each machine.
+
+
+.SH OPTIONS
+
+.TP
+.B \-h
+.TP
+.B \-\-help
+.br
+Print the summary of command line argument options.
+
+.TP
+.B \-v
+.TP
+.B \-\-version
+.br
+Print the installed version of mtr.  
+
+.TP
+.B \-r
+.TP
+.B \-\-report
+.br
+This option puts 
+.B mtr
+into 
+.B report
+mode.  When in this mode,
+.B mtr
+will run for the number of cycles specified by the 
+.B \-c
+option, and then print statistics and exit.  
+.TP
+\c
+This mode is useful for generating statistics about network quality.  
+Note that each running instance of 
+.B mtr
+generates a significant amount of network traffic.  Using 
+.B mtr
+at to measure the quality of your network may result in decreased
+network performance.  
+
+.TP
+.B \-c\ COUNT
+.TP
+.B \-\-report\-cycles\ COUNT
+Use this option to set the number of pings sent to determine
+both the machines on the network and the reliability of 
+those machines.  Each cycle lasts one second.  This option
+is only useful with the
+.B -r
+option. 
+
+.TP
+.B \-t
+.TP
+.B \-\-curses
+.br
+Use this option to force 
+.B mtr 
+to use the curses based terminal
+interface (if available).
+
+.TP
+.B \-g
+.TP
+.B \-\-gtk
+.br
+Use this option to force
+.B mtr 
+to use the GTK+ based X11 window interface (if available).  
+GTK+ must have been available on the system when 
+.B mtr 
+was built for this to work.  See the GTK+ web page at 
+.B http://www.gimp.org/gtk/
+for more information about GTK+.
+
+.TP
+.B \-i\ SECONDS
+.TP
+.B \-\-interval\ SECONDS
+.br
+Use this option to specify the positive number of seconds between ICMP
+ECHO requests.  The default value for this parameter is one second.
+
+.SH BUGS
+
+Some modern routers give a lower priority to ICMP ECHO packets than 
+to other network traffic.  Consequently, the reliability of these
+routers reported by 
+.B mtr
+will be significantly lower than the actual reliability of 
+these routers.  
+
+
+.SH CONTACT INFORMATION
+
+.PP
+For the latest version, see the mtr web page at 
+.BR http://www.mkimball.org/mtr.html .
+
+.PP
+Subscribe to the mtr mailing list.  All mtr related announcements
+are posted to the mtr mailing list.  To subscribe, send email to
+.B majordomo@lists.xmission.com
+with 
+.B subscribe\ mtr
+in the body of the message.  To send a message to the mailing list, mail to 
+.BR mtr@lists.xmission.com .
+
+.PP
+Bug reports and feature requests should be sent to the mtr
+mailing list.
+
+
+.SH "SEE ALSO"
+
+traceroute(8),
+ping(8).
diff --git a/mtr.c b/mtr.c
new file mode 100644 (file)
index 0000000..cb1dc8e
--- /dev/null
+++ b/mtr.c
@@ -0,0 +1,165 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mtr-curses.h"
+#include "getopt.h"
+#include "display.h"
+#include "dns.h"
+#include "report.h"
+#include "net.h"
+
+int DisplayMode;
+int Interactive = 1;
+int PrintVersion = 0;
+int PrintHelp = 0;
+int MaxPing = 16;
+float WaitTime = 1.0;
+char *Hostname = NULL;
+
+void parse_arg(int argc, char **argv) {
+  int opt;
+  static struct option long_options[] = {
+    { "version", 0, 0, 'v' },
+    { "help", 0, 0, 'h' },
+    { "report", 0, 0, 'r' },
+    { "report-cycles", 1, 0, 'c' },
+    { "curses", 0, 0, 't' },
+    { "gtk", 0, 0, 'g' },
+    { "interval", 1, 0, 'i' },
+    { 0, 0, 0, 0 }
+  };
+
+  opt = 0;
+  while(1) {
+    opt = getopt_long(argc, argv, "hvrc:tli:", long_options, NULL);
+    if(opt == -1)
+      break;
+
+    switch(opt) {
+    case 'v':
+      PrintVersion = 1;
+      break;
+    case 'h':
+      PrintHelp = 1;
+      break;
+    case 'r':
+      DisplayMode = DisplayReport;
+      break;
+    case 'c':
+      MaxPing = atoi(optarg);
+      break;
+    case 't':
+      DisplayMode = DisplayCurses;
+      break;
+    case 'g':
+      DisplayMode = DisplayGTK;
+      break;
+    case 'i':
+      WaitTime = atof(optarg);
+      if (WaitTime <= 0.0) {
+       fprintf (stderr, "mtr: wait time must be positive\n");
+       exit (-1);
+      }
+      break;
+    }
+  }
+
+  if(DisplayMode == DisplayReport)
+    Interactive = 0;
+
+  if(optind != argc - 1)
+    return;
+
+  Hostname = argv[optind];
+
+}
+
+int main(int argc, char **argv) {
+  int traddr;
+  struct hostent *host;
+
+  /*  Get the raw sockets first thing, so we can drop to user euid immediately  */
+  if(net_preopen() != 0) {
+    printf("mtr: Unable to get raw socket.  (Executable not suid?)\n");
+    exit(0);
+  }
+
+  /*  Now drop to user permissions  */
+  if(seteuid(getuid())) {
+    printf("mtr: Unable to drop permissions.\n");
+    exit(0);
+  }
+
+  /*  Double check, just in case  */
+  if(geteuid() != getuid()) {
+    printf("mtr: Unable to drop permissions.\n");
+    exit(0);
+  }
+  
+  display_detect(&argc, &argv);
+  parse_arg(argc, argv);
+
+  if(PrintVersion) {
+    printf("mtr " VERSION "\n");
+    exit(0);
+  }
+
+  if(Hostname == NULL || PrintHelp) {
+    printf("usage: %s [-hvrctli] [--help] [--version] [--report]\n"
+          "\t\t[--report-cycles=COUNT] [--curses] [--gtk]\n"
+          "\t\t[--interval=SECONDS] HOSTNAME\n", argv[0]);
+    exit(0);
+  }
+
+  host = gethostbyname(Hostname);
+  if(host == NULL) {
+#ifndef NO_HERROR
+    herror("mtr");
+#else
+    printf("mtr: error looking up \"%s\"\n", Hostname);
+#endif
+    exit(0);
+  }
+
+  traddr = *(int *)host->h_addr;
+
+  if(net_open(traddr) != 0) {
+    printf("mtr: Unable to get raw socket.  (Executable not suid?)\n");
+    exit(0);
+  }
+
+  display_open();
+  dns_open();
+
+  display_loop();
+
+  net_end_transit();
+  display_close();
+  net_close();
+
+  return 0;
+}
+
+
diff --git a/net.c b/net.c
new file mode 100644 (file)
index 0000000..1dc7372
--- /dev/null
+++ b/net.c
@@ -0,0 +1,428 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+
+#if defined(HAVE_SYS_XTI_H)
+#include <sys/xti.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <memory.h>
+#include <unistd.h>
+
+#include "net.h"
+
+#define MaxHost 256
+#define MaxTransit 4
+
+/*  We can't rely on header files to provide this information, because
+    the fields have different names between, for instance, Linux and 
+    Solaris  */
+struct ICMPHeader {
+  unsigned char type;
+  unsigned char code;
+  unsigned short checksum;
+  unsigned short id;
+  unsigned short sequence;
+};
+
+/*  Structure of an IP header.  */
+struct IPHeader {
+  uint8 version;
+  uint8 tos;
+  uint16 len;
+  uint16 id;
+  uint16 frag;
+  uint8 ttl;
+  uint8 protocol;
+  uint16 check;
+  uint32 saddr;
+  uint32 daddr;
+};
+  
+#define ICMP_ECHO              8
+#define ICMP_ECHOREPLY         0
+#define ICMP_TIME_EXCEEDED     11
+  
+#ifndef SOL_IP
+#define SOL_IP 0
+#endif
+  
+struct packetdata {
+    int index;
+    int ttl;
+    int sec;
+    int msec;
+};
+
+struct nethost {
+  int addr;
+  int xmit;
+  int returned;
+  int total;
+  int best;
+  int worst;
+  int transit;
+};
+
+static struct nethost host[MaxHost];
+static struct timeval reset = { 0, 0 };
+
+int sendsock;
+int recvsock;
+struct sockaddr_in remoteaddress;
+
+int checksum(void *data, int sz) {
+  unsigned short *ch;
+  unsigned int sum;
+
+  sum = 0;
+  ch = data;
+  sz = sz / 2;
+  while(sz--) {
+    sum += *(ch++);
+  }
+  
+  sum = (sum >> 16) + (sum & 0xffff);  
+
+  return (~sum & 0xffff);  
+}
+
+void net_send_ping(int index) {
+  char packet[sizeof(struct IPHeader) + sizeof(struct ICMPHeader) 
+            + sizeof(struct packetdata)];
+  struct IPHeader *ip;
+  struct ICMPHeader *icmp;
+  struct packetdata *data;
+  int packetsize = sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata);
+  struct sockaddr_in addr;
+  struct timeval now;
+
+  memset(&addr, 0, sizeof(struct sockaddr_in));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = host[index].addr;
+  host[index].xmit++;
+  host[index].transit = 1;
+
+  memset(packet, 0, packetsize);
+
+  ip = (struct IPHeader *)packet;
+  icmp = (struct ICMPHeader *)(packet + sizeof(struct IPHeader));
+  data = (struct packetdata *)(packet + sizeof(struct IPHeader) + sizeof(struct ICMPHeader));
+
+  ip->version = 0x45;
+  ip->tos = 0;
+  ip->len = packetsize;
+  ip->id = 0;
+  ip->frag = 0;
+  ip->ttl = 127;
+  ip->protocol = IPPROTO_ICMP;
+  ip->saddr = 0;
+  ip->daddr = host[index].addr;
+  
+  icmp->type = ICMP_ECHO;
+  icmp->id = getpid();
+  icmp->sequence = 0;
+
+  data->ttl = 0;
+  data->index = index;
+
+  gettimeofday(&now, NULL);
+  data->sec = now.tv_sec;
+  data->msec = now.tv_usec / 1000;
+
+  icmp->checksum = checksum(icmp, packetsize - sizeof(struct IPHeader));
+  ip->check = checksum(ip, packetsize);
+
+  sendto(sendsock, packet, packetsize, 0, 
+        (struct sockaddr *)&addr, sizeof(addr));
+}
+
+/*  Attempt to find the host at a particular number of hops away  */
+void net_send_query(int hops) {
+  char packet[sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata)];
+  struct IPHeader *ip;
+  struct ICMPHeader *icmp;
+  struct packetdata *data;
+  int packetsize = sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata);
+
+  memset(packet, 0, packetsize);
+
+  ip = (struct IPHeader *)packet;
+  icmp = (struct ICMPHeader *)(packet + sizeof(struct IPHeader));
+  data = (struct packetdata *)(packet + sizeof(struct IPHeader) + sizeof(struct ICMPHeader));
+
+  ip->version = 0x45;
+  ip->tos = 0;
+  ip->len = packetsize;
+  ip->id = 0;
+  ip->frag = 0;
+  ip->ttl = hops;
+  ip->protocol = IPPROTO_ICMP;
+  ip->saddr = 0;
+  ip->daddr = remoteaddress.sin_addr.s_addr;
+
+  icmp->type = ICMP_ECHO;
+  icmp->id = getpid();
+  icmp->sequence = hops;
+
+  data->ttl = hops;
+  data->index = -1;
+
+  icmp->checksum = checksum(icmp, packetsize - sizeof(struct IPHeader));
+  ip->check = checksum(ip, packetsize);
+
+  sendto(sendsock, packet, packetsize, 0, 
+        (struct sockaddr *)&remoteaddress, sizeof(remoteaddress));
+}
+
+void net_process_ping(struct packetdata *data, struct sockaddr_in *addr) {
+  int at;
+  struct timeval now;
+  int totmsec;
+  int msec;
+
+  if(data->index >= 0) {
+    gettimeofday(&now, NULL);
+
+    if(data->sec < reset.tv_sec
+       || (data->sec == reset.tv_sec && (1000*data->msec) < reset.tv_usec))
+      /* discard this data point, stats were reset after it was generated */
+      return;
+    
+    totmsec = (now.tv_sec - data->sec) * 1000;
+    msec = now.tv_usec / 1000 - data->msec;
+    if(msec >= 0) 
+      totmsec += msec;
+    else
+      totmsec = totmsec - 1000 + 1000 - data->msec + now.tv_usec / 1000;
+
+    if(host[data->index].returned <= 0) {
+      host[data->index].best = host[data->index].worst = totmsec;
+    }
+
+    if(totmsec < host[data->index].best)
+      host[data->index].best = totmsec;
+
+    if(totmsec > host[data->index].worst)
+      host[data->index].worst = totmsec;
+
+    host[data->index].total += totmsec;
+    host[data->index].returned++;
+    host[data->index].transit = 0;
+  } else {
+    at = data->ttl - 1;
+    if(at < 0 || at > MaxHost)
+      return;
+
+    host[at].addr = addr->sin_addr.s_addr;
+  }
+}
+
+void net_process_return() {
+  char packet[2048];
+  struct sockaddr_in fromaddr;
+  int fromaddrsize;
+  int num;
+  int at;
+  struct ICMPHeader *header;
+
+  fromaddrsize = sizeof(fromaddr);
+  num = recvfrom(recvsock, packet, 2048, 0, 
+                (struct sockaddr *)&fromaddr, &fromaddrsize);
+
+  if(num < sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata))
+    return;
+
+  header = (struct ICMPHeader *)(packet + sizeof(struct IPHeader));
+  if(header->type == ICMP_ECHOREPLY) {
+    if(header->id != getpid())
+      return;
+
+    net_process_ping((struct packetdata *)(packet + sizeof(struct IPHeader) + 
+                                          sizeof(struct ICMPHeader)),
+                    &fromaddr);
+  } else if(header->type == ICMP_TIME_EXCEEDED) {
+    if(num < sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + 
+             sizeof(struct IPHeader) + sizeof(struct ICMPHeader))
+      return;
+    
+    header = (struct ICMPHeader *)(packet + sizeof(struct IPHeader) + 
+                               sizeof(struct ICMPHeader) + sizeof(struct IPHeader));
+    if(header->id != getpid())
+      return;
+    
+    at = header->sequence - 1;
+    if(at < 0 || at > MaxHost)
+      return;
+
+    host[at].addr = fromaddr.sin_addr.s_addr;
+  }
+}
+
+int net_addr(int at) {
+  return ntohl(host[at].addr);
+}
+
+int net_percent(int at) {
+  if(host[at].xmit == 0) 
+    return 0;
+
+  return 100 - (100 * (host[at].returned + host[at].transit) / host[at].xmit);
+}
+
+int net_best(int at) {
+  return host[at].best;
+}
+
+int net_worst(int at) {
+  return host[at].worst;
+}
+
+int net_avg(int at) {
+  if(host[at].returned == 0)
+    return 0;
+
+  return host[at].total / host[at].returned;
+}
+
+int net_max() {
+  int at;
+  int max;
+
+  max = 0;
+  for(at = 0; at < MaxHost; at++) {
+    if(host[at].addr == remoteaddress.sin_addr.s_addr) {
+      return at + 1;
+    } else if(host[at].addr != 0) {
+      max = at + 2;
+    }
+  }
+
+  return max;
+}
+
+
+/* Added by Brian Casey December 1997 bcasey@imagiware.com*/
+int net_returned(int at) { 
+   return host[at].returned;
+}
+int net_xmit(int at) { 
+   return host[at].xmit;
+}
+int net_transit(int at) { 
+   return host[at].transit;
+}
+
+void net_end_transit() {
+  int at;
+
+  for(at = 0; at < MaxHost; at++) {
+    host[at].transit = 0;
+  }
+}
+
+void net_send_batch() {
+  int at;
+  int n_unknown = 10;
+
+  for(at = 0;n_unknown && (at < MaxHost); at++) {
+    if(host[at].addr == 0) {
+      net_send_query(at + 1);
+      n_unknown--;
+    } else {
+      net_send_ping(at);
+    }
+
+    if(host[at].addr == remoteaddress.sin_addr.s_addr) {
+      break;
+    }
+  }
+}
+
+int net_preopen() {
+  char trueopt = 1;
+
+  sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+  if(sendsock == -1)
+    return -1;
+
+#ifdef IP_HDRINCL
+  /*  FreeBSD wants this to avoid sending out packets with protocol type RAW
+      to the network.  */
+  if(setsockopt(sendsock, 0, IP_HDRINCL, &trueopt, sizeof(trueopt)))
+    return -1;
+#endif
+
+  recvsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  if(recvsock == -1)
+    return -1;  
+
+  return 0;
+}
+int net_open(int addr) {
+  remoteaddress.sin_family = AF_INET;
+  remoteaddress.sin_addr.s_addr = addr;
+
+  net_send_batch();
+
+  return 0;
+}
+
+void net_reopen(int addr) {
+  int at;
+
+  for(at = 0; at < MaxHost; at++) {
+    memset(&host[at], 0, sizeof(host[at]));
+  }
+
+  remoteaddress.sin_family = AF_INET;
+  remoteaddress.sin_addr.s_addr = addr;
+
+  net_send_batch();
+}
+
+void net_reset() {
+  int at;
+
+  for(at = 0; at < MaxHost; at++) {
+    host[at].xmit = host[at].transit;
+    host[at].returned = 0;
+    host[at].total = 0;
+    host[at].best = 0;
+    host[at].worst = 0;
+  }
+  gettimeofday(&reset, NULL);
+}
+
+void net_close() {
+  close(sendsock);
+  close(recvsock);
+}
+
+int net_waitfd() {
+  return recvsock;
+}
+
+
diff --git a/net.h b/net.h
new file mode 100644 (file)
index 0000000..cae195e
--- /dev/null
+++ b/net.h
@@ -0,0 +1,43 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*  Prototypes for functions in net.c  */
+
+int net_preopen();
+int net_open(int address);
+void net_reopen(int address);
+void net_reset();
+void net_close();
+int net_waitfd();
+void net_process_return();
+int net_max();
+int net_addr(int at);
+int net_percent(int at);
+int net_best(int at);
+int net_worst(int at);
+int net_avg(int at);
+void net_send_batch();
+void net_end_transit();
+
+/* Added by Brian Casey, December 1997 bcasey@imagiware.com*/
+int net_returned(int at);
+int net_xmit(int at);
+int net_transit(int at);
+
+
diff --git a/report.c b/report.c
new file mode 100644 (file)
index 0000000..9181781
--- /dev/null
+++ b/report.c
@@ -0,0 +1,71 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include "report.h"
+#include "net.h"
+
+void report_open() {
+  printf("%-40s LOSS  RCVD  SENT BEST   AVG  WORST\n", "HOST");
+  fflush(stdout);
+}
+
+void report_close() {
+  int at, max, addr;
+  int haddr;
+  char name[81];
+  struct hostent *host;
+
+  max = net_max();
+
+  for(at = 0; at < max; at++) {
+    addr = net_addr(at);
+    
+    strcpy(name, "");
+    if(addr == 0) {
+      sprintf(name, "???");
+    } else {
+      haddr = htonl(addr);
+      host = gethostbyaddr((char *)&haddr, sizeof(int), AF_INET);
+
+      if(host != NULL) {
+        strncpy(name, host->h_name, 80);
+        name[80] = 0;
+      } else {
+       sprintf(name, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, 
+               (addr >> 8) & 0xff, addr & 0xff);
+      }
+    }
+
+    printf("%-40s%5d%%%6d%5d%6d%6d%6d\n", name,     
+            net_percent(at),
+             net_returned(at), net_xmit(at),
+             net_best(at), net_avg(at), net_worst(at));
+  }
+}
+
+
+
diff --git a/report.h b/report.h
new file mode 100644 (file)
index 0000000..8a8a9b6
--- /dev/null
+++ b/report.h
@@ -0,0 +1,23 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*  Prototypes for report.h  */
+
+void report_open();
+void report_close();
diff --git a/select.c b/select.c
new file mode 100644 (file)
index 0000000..f68df52
--- /dev/null
+++ b/select.c
@@ -0,0 +1,147 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <math.h>
+
+#include "display.h"
+#include "dns.h"
+#include "net.h"
+
+extern int Interactive;
+extern int MaxPing;
+extern float WaitTime;
+double dnsinterval;
+
+void select_loop() {
+  fd_set readfd;
+  int anyset;
+  int action, maxfd;
+  int dnsfd, netfd;
+  int NumPing;
+  struct timeval lasttime, thistime, selecttime, intervaltime;
+
+  NumPing = 0;
+  anyset = 0;
+  gettimeofday(&lasttime, NULL);
+  intervaltime.tv_sec = (int)WaitTime;
+  intervaltime.tv_usec = 1000000.0 * (WaitTime - floor(WaitTime));
+
+  while(1) {
+    FD_ZERO(&readfd);
+
+    maxfd = 0;
+
+    if(Interactive) {
+      FD_SET(0, &readfd);
+      maxfd = 1;
+    }
+
+    dnsfd = dns_waitfd();
+    FD_SET(dnsfd, &readfd);
+    if(dnsfd >= maxfd)
+      maxfd = dnsfd + 1;
+
+    netfd = net_waitfd();
+    FD_SET(netfd, &readfd);
+    if(netfd >= maxfd)
+      maxfd = netfd + 1;
+
+    if(anyset) {
+      selecttime.tv_sec = 0;
+      selecttime.tv_usec = 0;
+      
+      select(maxfd, (void *)&readfd, NULL, NULL, &selecttime);
+    } else {
+      if(Interactive) 
+       display_redraw();
+
+      gettimeofday(&thistime, NULL);
+
+      if(thistime.tv_sec > lasttime.tv_sec + intervaltime.tv_sec ||
+         (thistime.tv_sec == lasttime.tv_sec + intervaltime.tv_sec &&
+          thistime.tv_usec >= lasttime.tv_usec + intervaltime.tv_usec)) {
+        lasttime = thistime;
+        if(NumPing >= MaxPing && !Interactive)
+          break;
+        NumPing++;
+        net_send_batch();
+      }
+
+      selecttime.tv_usec = (thistime.tv_usec - lasttime.tv_usec);
+      selecttime.tv_sec = (thistime.tv_sec - lasttime.tv_sec);
+      if (selecttime.tv_usec < 0) {
+       --selecttime.tv_sec;
+       selecttime.tv_usec += 1000000;
+      }
+      selecttime.tv_usec = intervaltime.tv_usec - selecttime.tv_usec;
+      selecttime.tv_sec = intervaltime.tv_sec - selecttime.tv_sec;
+      if (selecttime.tv_usec < 0) {
+       --selecttime.tv_sec;
+       selecttime.tv_usec += 1000000;
+      }
+
+      if ((selecttime.tv_sec > (time_t)dnsinterval) ||
+          ((selecttime.tv_sec == (time_t)dnsinterval) &&
+           (selecttime.tv_usec > ((time_t)(dnsinterval * 1000000) % 1000000)))) {
+        selecttime.tv_sec = (time_t)dnsinterval;
+        selecttime.tv_usec = (time_t)(dnsinterval * 1000000) % 1000000;
+      }
+
+      select(maxfd, (void *)&readfd, NULL, NULL, &selecttime);
+    }
+
+    anyset = 0;
+
+    /* Handle any pending resolver events */
+    dnsinterval = WaitTime;
+    dns_events(&dnsinterval);
+
+    /*  Has a key been pressed?  */
+    if(FD_ISSET(0, &readfd)) {
+      action = display_keyaction();
+
+      if(action == ActionQuit)
+       break;
+
+      if(action == ActionReset) 
+       net_reset();
+
+      anyset = 1;
+    }
+
+    /*  Have we finished a nameservice lookup?  */
+    if(FD_ISSET(dnsfd, &readfd)) {
+      dns_ack();
+      anyset = 1;
+    }
+
+    /*  Have we got new packets back?  */
+    if(FD_ISSET(netfd, &readfd)) {
+      net_process_return();
+      anyset = 1;
+    }
+  }
+}
+
diff --git a/select.h b/select.h
new file mode 100644 (file)
index 0000000..33a9a96
--- /dev/null
+++ b/select.h
@@ -0,0 +1,20 @@
+/*
+    mtr  --  a network diagnostic tool
+    Copyright (C) 1997,1998  Matt Kimball
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+void select_loop();