--- /dev/null
+ 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.
--- /dev/null
+ 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.
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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)
+
--- /dev/null
+/*
+ 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();
+}
--- /dev/null
+/*
+ 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
+ }
+}
+
--- /dev/null
+/*
+ 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();
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+/*
+ 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);
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+/*
+ 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();
+}
--- /dev/null
+EXTRA_DIST = mtr_icon.xpm
--- /dev/null
+/* 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 "};
--- /dev/null
+/*
+ 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();
--- /dev/null
+/*
+ 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();
--- /dev/null
+.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).
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ 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);
+
+
--- /dev/null
+/*
+ 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));
+ }
+}
+
+
+
--- /dev/null
+/*
+ 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();
--- /dev/null
+/*
+ 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;
+ }
+ }
+}
+
--- /dev/null
+/*
+ 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();