From: Matt Kimball Date: Sat, 17 Oct 1998 00:00:00 +0000 (+0000) Subject: mtr v0.21 X-Git-Tag: v0.21^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=090a9cfe7175ebc17bbcbab9e6cdbb904eaa7165;p=thirdparty%2Fmtr.git mtr v0.21 v0.21 - mtr now drops root permissions after it acquires the raw sockets it needs. - mtr should be a bit happier about building under SCO and Solaris. - Fixed the problem with packets arriving after a reset. v0.20 - The build process for mtr now uses automake. - Fixed a build problem for Irix. - Now uses non-blocking DNS code, so mtr can attempt to do reverse lookup on multiple hosts at once. - Fewer packets are sent out each cycle, so mtr doesn't hog quite so much bandwidth. v0.19 - Fixed a type-o in curses.c v0.18 - Fixed the network code to work properly under FreeBSD. - Hopefully this will fix some other operating systems too. - Also, fixed a build problem and the DNS hanging bug. v0.17 - Fixed the configure script to always like with the math library. Added an icon. v0.16 - Added one #include to select.c. Some people were unable to build mtr without this line. v0.15 - Both the build process and the networking code have been cleaned up and reorganized. mtr now builds cleanly with GTK+ 0.99.8. 2002-03-06 Cougar - If hop doesn't respond, draw its name in red (GTK) or bold (curses) 2002-02-09 Bohdan Vlasyuk - Added --address option to bind to given IP addess 2001-04-15 Alan Eldridge - Added this file so that automake won't complain. Commented out the test for res_init in configure.in; it does not work for GLIBC2 systems (e.g., RedHat 7+). - Fixed the subordinate CHECK_LIBS on the test for res_mkquery, so that they test for res_mkquery, not res_init. source: ftp://ftp.bitwizard.nl/mtr/mtr-0.21.tar.gz --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..c3ecbfa --- /dev/null +++ b/AUTHORS @@ -0,0 +1,13 @@ + Matt Kimball is the primary author of mtr. + + Bug reports and feature requests should be sent to the mtr + mailing list. See the README file for details. + + Thanks to everyone who has provided feedback on mtr. + + Thanks especially to those of you who have sent me code: + + Brian Casey, Mircea Damian, Christophe Kalt, Simon Kirby, + Anand Kumria, Charles Levert, Russell Nelson, + Aaron Scarisbrick, Andrew Stesin, Juha Takala, Rogier Wolff + and anyone who has slipped through the cracks of my mail file. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d5648d9 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = img + +sbin_PROGRAMS = mtr +man_MANS = mtr.8 +install-exec-am: + chmod u+s $(sbindir)/mtr + +mtr_SOURCES = mtr.c \ + net.c net.h \ + dns.c dns.h \ + display.c display.h \ + report.c report.h \ + getopt.c getopt1.c getopt.h \ + select.c select.h \ + mtr-curses.h \ + mtr-gtk.h +EXTRA_mtr_SOURCES = curses.c \ + gtk.c +mtr_DEPENDENCIES = $(GTK_OBJ) $(CURSES_OBJ) +mtr_LDFLAGS = $(GTK_OBJ) $(CURSES_OBJ) + +EXTRA_DIST = SECURITY mtr.8 diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..08886b9 --- /dev/null +++ b/NEWS @@ -0,0 +1,37 @@ +WHAT'S NEW? + + v0.21 + mtr now drops root permissions after it acquires the raw + sockets it needs. + mtr should be a bit happier about building under SCO and + Solaris. + Fixed the problem with packets arriving after a reset. + + v0.20 + The build process for mtr now uses automake. + Fixed a build problem for Irix. + Now uses non-blocking DNS code, so mtr can attempt + to do reverse lookup on multiple hosts at once. + Fewer packets are sent out each cycle, so mtr + doesn't hog quite so much bandwidth. + + v0.19 + Fixed a type-o in curses.c + + v0.18 + Fixed the network code to work properly under FreeBSD. + Hopefully this will fix some other operating systems too. + Also, fixed a build problem and the DNS hanging bug. + + v0.17 + Fixed the configure script to always like with the math + library. Added an icon. + + v0.16 + Added one #include to select.c. Some people were unable + to build mtr without this line. + + v0.15 + Both the build process and the networking code have + been cleaned up and reorganized. mtr now builds + cleanly with GTK+ 0.99.8. diff --git a/README b/README new file mode 100644 index 0000000..42d7c52 --- /dev/null +++ b/README @@ -0,0 +1,44 @@ +WHAT IS MTR? + + mtr combines the functionaly of the 'traceroute' and 'ping' programs + in a single network diagnostic tool. + + As mtr starts, it investigates the network connection between the host + mtr runs on and a user-specified destination host. After it + determines the address of each network hop between the machines, + it sends a sequence ICMP ECHO requests to each one to determine the + quality of the link to each machine. As it does this, it prints + running statistics about each machine. + + mtr is distributed under the GNU General Public License. + See the COPYING file for details. + +INSTALLING + + To begin mtr, first use the included configure script: + + ./configure + + The configure script will generate a Makefile. Build mtr: + + make + + After compiling, install: + + make install + + Note that mtr must be suid-root because it requires access to raw IP + sockets. See SECURITY for security information. + +WHERE CAN I GET THE LATEST VERSION OR MORE INFORMATION? + + See the mtr web page at 'http://www.mkimball.org/mtr.html'. + + Subscribe to the mtr mailing list. All mtr related announcements + are posted to the mtr mailing list. To subscribe, send email to + 'majordomo@lists.xmission.com' with 'subscribe mtr' in the body of + the message. To send a message to the mailing list, mail to + 'mtr@lists.xmission.com'. + + Bug reports and feature requests should be sent to the mtr + mailing list. diff --git a/SECURITY b/SECURITY new file mode 100644 index 0000000..d81a954 --- /dev/null +++ b/SECURITY @@ -0,0 +1,27 @@ +SECURITY ISSUES RELATED TO MTR + +Since mtr is installed as suid-root, some concern over security is +justified. Since version 0.21 of mtr, does the following two things +after it is launched: + +* mtr requests a pair of raw sockets from the kernel. +* mtr sets the effective uid to match the real uid. + +See main() in mtr.c and net_preopen() in net.c for the details of this +process. Note that no code from GTK+ or curses is executed before the +drop in permissions. + +This should severely limit the possibilities of using mtr to breach +system security. This means the worst case scenerio is as follows: + +Due to some oversight in the mtr code, a malicious user is able to +overrun one of mtr's internal buffers with binary code that is +eventually executed. The malicious user is still not able to read +from or write to any system files which they wouldn't normally have +permission to write to. The only priveledge gained is access to the +raw socket descriptors, which would allow the malicious user to listen +to all ICMP packets arriving at the system, and send forged packets +with arbitrary ncontents. + +If you have further questions or comments about security issues, +please direct them to the mtr mailing list. See README for details. diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..482bd5e --- /dev/null +++ b/configure.in @@ -0,0 +1,48 @@ +AC_INIT(mtr.c) +AM_INIT_AUTOMAKE(mtr, 0.21) + +AC_SUBST(GTK_OBJ) +AC_SUBST(CURSES_OBJ) + +GTK_OBJ=gtk.o +CURSES_OBJ=curses.o + +AC_PROG_CC + +AC_CHECK_SIZEOF(unsigned char, 1) +AC_CHECK_SIZEOF(unsigned short, 2) +AC_CHECK_SIZEOF(unsigned int, 4) +AC_CHECK_SIZEOF(unsigned long, 4) + +AC_CHECK_FUNC(initscr, , + AC_CHECK_LIB(ncurses, initscr, , + AC_CHECK_LIB(curses, initscr, , + AC_MSG_WARN(Building without curses display support) + AC_DEFINE(NO_CURSES) + CURSES_OBJ=))) +AC_CHECK_HEADERS(ncurses.h ncurses/curses.h curses.h) +AC_CHECK_HEADERS(sys/xti.h) + +AC_CHECK_LIB(m, floor, , AC_MSG_ERROR(No math library found)) + +AM_PATH_GTK(1.0.0, CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS", + AC_MSG_WARN(Building without GTK+ display support) + AC_DEFINE(NO_GTK) + GTK_OBJ=) + +AC_CHECK_FUNC(socket, , + AC_CHECK_LIB(socket, socket, , AC_MSG_ERROR(No socket library found))) + +AC_CHECK_FUNC(gethostbyname, , + AC_CHECK_LIB(nsl, gethostbyname, , AC_MSG_ERROR(No nameservice library found))) + +AC_CHECK_FUNC(res_mkquery, , + AC_CHECK_LIB(resolv, res_mkquery, , AC_MSG_ERROR(No resolver library found))) + +AC_CHECK_FUNC(herror, , AC_DEFINE(NO_HERROR)) +AC_CHECK_FUNC(strerror, , AC_DEFINE(NO_STRERROR)) + +AM_CONFIG_HEADER(config.h) +AC_OUTPUT(Makefile img/Makefile) + diff --git a/curses.c b/curses.c new file mode 100644 index 0000000..f49da44 --- /dev/null +++ b/curses.c @@ -0,0 +1,160 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#ifndef NO_CURSES +#include +#include + +#if defined(HAVE_NCURSES_H) +# include +#elif defined(HAVE_NCURSES_CURSES_H) +# include +#elif defined(HAVE_CURSES_H) +# include +#else +# error No curses header file available +#endif + +#ifndef getmaxyx +# define getmaxyx(win,y,x) (y = (win)->_maxy + 1, x = (win)->_maxx + 1) +#endif + +#include "mtr-curses.h" +#include "display.h" +#include "net.h" +#include "dns.h" +#endif + +void pwcenter(char *str) { + int maxx, maxy; + int cx; + + getmaxyx(stdscr, maxy, maxx); + cx = (signed)(maxx - strlen(str)) / 2; + while(cx-- > 0) + printw(" "); + printw(str); +} + +int mtr_curses_keyaction() { + char c = getch(); + + if(tolower(c) == 'q') + return ActionQuit; + if(c==3) + return ActionQuit; + if(tolower(c) == 'r') + return ActionReset; + + return 0; +} + +void mtr_curses_hosts(int startstat) { + int max; + int at; + int addr; + int y, x; + char *name; + + max = net_max(); + + for(at = 0; at < max; at++) { + printw("%2d. ", at + 1); + addr = net_addr(at); + + if(addr != 0) { + name = dns_lookup(addr); + if(name != NULL) { + printw("%s", name); + } else { + printw("%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + } + + getyx(stdscr, y, x); + move(y, startstat); + + printw(" %3d%% %4d%4d %5d%5d%7d", + net_percent(at), + net_returned(at), net_xmit(at), + net_best(at), net_avg(at), net_worst(at)); + + + } else { + printw("???"); + } + + printw("\n"); + } +} + +void mtr_curses_redraw() { + int maxx, maxy; + int startstat; + int rowstat; + + erase(); + getmaxyx(stdscr, maxy, maxx); + + /* Modified by Brian Casey December 1997 bcasey@imagiware.com */ + startstat = maxx - 40; + + rowstat = 5; + + attron(A_BOLD); + move(0, 0); + pwcenter("Matt's traceroute [v" VERSION "]"); + printw("\n\n"); + attroff(A_BOLD); + + printw("Keys: "); + attron(A_BOLD); printw("R"); attroff(A_BOLD); + printw(" - Restart statistics "); + attron(A_BOLD); printw("Q"); attroff(A_BOLD); + printw(" - Quit\n"); + + attron(A_BOLD); + mvprintw(rowstat - 1, 0, "Hostname"); + + /* Modified by Brian Casey December 1997 bcasey@imagiware.com */ + mvprintw(rowstat - 2, startstat, " Packets Pings"); + mvprintw(rowstat - 1, startstat, " %%Loss Rcv Snt Best Avg Worst"); + + attroff(A_BOLD); + move(rowstat, 0); + + mtr_curses_hosts(startstat); + + refresh(); +} + +void mtr_curses_open() { + initscr(); + raw(); + noecho(); + + mtr_curses_redraw(); +} + +void mtr_curses_close() { + printw("\n"); + endwin(); +} diff --git a/display.c b/display.c new file mode 100644 index 0000000..f140aa9 --- /dev/null +++ b/display.c @@ -0,0 +1,128 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include "display.h" +#include "mtr-curses.h" +#include "mtr-gtk.h" +#include "report.h" +#include "select.h" + +extern int DisplayMode; + +void display_detect(int *argc, char ***argv) { + DisplayMode = DisplayReport; + +#ifndef NO_CURSES + DisplayMode = DisplayCurses; +#endif + +#ifndef NO_GTK + if(gtk_detect(argc, argv)) { + DisplayMode = DisplayGTK; + } +#endif +} + +void display_open() { + switch(DisplayMode) { + case DisplayReport: + report_open(); + break; + +#ifndef NO_CURSES + case DisplayCurses: + mtr_curses_open(); + break; +#endif + +#ifndef NO_GTK + case DisplayGTK: + gtk_open(); + break; +#endif + } +} + +void display_close() { + switch(DisplayMode) { + case DisplayReport: + report_close(); + break; + +#ifndef NO_CURSES + case DisplayCurses: + mtr_curses_close(); + break; +#endif + +#ifndef NO_GTK + case DisplayGTK: + gtk_close(); + break; +#endif + } +} + +void display_redraw() { + switch(DisplayMode) { +#ifndef NO_CURSES + case DisplayCurses: + mtr_curses_redraw(); + break; +#endif + +#ifndef NO_GTK + case DisplayGTK: + gtk_redraw(); + break; +#endif + } +} + +int display_keyaction() { + switch(DisplayMode) { +#ifndef NO_CURSES + case DisplayCurses: + return mtr_curses_keyaction(); +#endif + +#ifndef NO_GTK + case DisplayGTK: + return gtk_keyaction(); +#endif + } + return 0; +} + +void display_loop() { + switch(DisplayMode) { + case DisplayCurses: + case DisplayReport: + select_loop(); + break; + +#ifndef NO_GTK + case DisplayGTK: + gtk_loop(); + break; +#endif + } +} + diff --git a/display.h b/display.h new file mode 100644 index 0000000..9d89d56 --- /dev/null +++ b/display.h @@ -0,0 +1,29 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +enum { ActionNone, ActionQuit, ActionReset }; +enum { DisplayReport, DisplayCurses, DisplayGTK }; + +/* Prototypes for display.c */ +void display_detect(int *argc, char ***argv); +void display_open(); +void display_close(); +void display_redraw(); +int display_keyaction(); +void display_loop(); diff --git a/dns.c b/dns.c new file mode 100644 index 0000000..42c1c75 --- /dev/null +++ b/dns.c @@ -0,0 +1,1157 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Non-blocking DNS portion -- + Copyright (C) 1998 by Simon Kirby + Released under GPL, as above. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NO_STRERROR +extern int sys_nerr; +extern char *sys_errlist[]; +#define strerror(errno) (((errno) >= 0 && (errno) < sys_nerr) ? sys_errlist[errno] : "unlisted error") +#endif + +/* Hmm, it seems Irix requires this */ +extern int errno; + +/* Defines */ + +#undef Debug + +#undef CorruptCheck +#undef WipeFrees +#undef WipeMallocs + +#define BashSize 8192 /* Size of hash tables */ +#define BashModulo(x) ((x) & 8191) /* Modulo for hash table size: */ +#define HostnameLength 255 /* From RFC */ +#define ResRetryDelay1 3 +#define ResRetryDelay2 4 +#define ResRetryDelay3 5 + +/* Macros */ + +#define nonull(s) (s) ? s : nullstring + +/* Typedefs */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +typedef unsigned int ip_t; + +/* Structures */ + +struct resolve { + struct resolve *next; + struct resolve *previous; + struct resolve *nextid; + struct resolve *previousid; + struct resolve *nextip; + struct resolve *previousip; + struct resolve *nexthost; + struct resolve *previoushost; + char *hostname; + double expiretime; + ip_t ip; + word id; + byte state; +}; + +/* Non-blocking nameserver interface routines */ + +#define MaxPacketsize (PACKETSZ) +#define DomainLength (MAXDNAME) + +#define OpcodeCount 3 +char *opcodes[OpcodeCount+1] = { + "standard query", + "inverse query", + "server status request", + "unknown", +}; + +#define ResponsecodeCount 6 +char *responsecodes[ResponsecodeCount+1] = { + "no error", + "format error in query", + "server failure", + "queried domain name does not exist", + "requested query type not implemented", + "refused by name server", + "unknown error", +}; + +#define ResourcetypeCount 17 +char *resourcetypes[ResourcetypeCount+1] = { + "unknown type", + "A: host address", + "NS: authoritative name server", + "MD: mail destination (OBSOLETE)", + "MF: mail forwarder (OBSOLETE)", + "CNAME: name alias", + "SOA: authority record", + "MB: mailbox domain name (EXPERIMENTAL)", + "MG: mail group member (EXPERIMENTAL)", + "MR: mail rename domain name (EXPERIMENTAL)", + "NULL: NULL RR (EXPERIMENTAL)", + "WKS: well known service description", + "PTR: domain name pointer", + "HINFO: host information", + "MINFO: mailbox or mail list information", + "MX: mail exchange", + "TXT: text string", + "unknown type", +}; + +#define ClasstypeCount 5 +char *classtypes[ClasstypeCount+1] = { + "unknown class", + "IN: the Internet", + "CS: CSNET (OBSOLETE)", + "CH: CHAOS", + "HS: Hesoid [Dyer 87]", + "unknown class" +}; + +char *rrtypes[] = { + "Unknown", + "Query", + "Answer", + "Authority reference", + "Resource reference", +}; + +enum { + RR_UNKNOWN, + RR_QUERY, + RR_ANSWER, + RR_AUTHORITY, + RR_RESOURCE, +}; + +typedef struct { + word id; /* Packet id */ + byte databyte_a; + /* rd:1 recursion desired + * tc:1 truncated message + * aa:1 authoritive answer + * opcode:4 purpose of message + * qr:1 response flag + */ + byte databyte_b; + /* rcode:4 response code + * unassigned:2 unassigned bits + * pr:1 primary server required (non standard) + * ra:1 recursion available + */ + word qdcount; /* Query record count */ + word ancount; /* Answer record count */ + word nscount; /* Authority reference record count */ + word arcount; /* Resource reference record count */ +} packetheader; + +#ifndef HFIXEDSZ +#define HFIXEDSZ (sizeof(packetheader)) +#endif + +/* + * Byte order independent macros for packetheader + */ +#define getheader_rd(x) (x->databyte_a & 1) +#define getheader_tc(x) ((x->databyte_a >> 1) & 1) +#define getheader_aa(x) ((x->databyte_a >> 2) & 1) +#define getheader_opcode(x) ((x->databyte_a >> 3) & 15) +#define getheader_qr(x) (x->databyte_a >> 7) +#define getheader_rcode(x) (x->databyte_b & 15) +#define getheader_pr(x) ((x->databyte_b >> 6) & 1) +#define getheader_ra(x) (x->databyte_b >> 7) + +#define sucknetword(x) (((word)*(x) << 8) | (((x)+= 2)[-1])) +#define sucknetshort(x) (((short)*(x) << 8) | (((x)+= 2)[-1])) +#define sucknetdword(x) (((dword)*(x) << 24) | ((x)[1] << 16) | ((x)[2] << 8) | (((x)+= 4)[-1])) +#define sucknetlong(x) (((long)*(x) << 24) | ((x)[1] << 16) | ((x)[2] << 8) | (((x)+= 4)[-1])) + +enum { + STATE_FINISHED, + STATE_FAILED, + STATE_PTRREQ1, + STATE_PTRREQ2, + STATE_PTRREQ3, +}; + +#define Is_PTR(x) ((x->state == STATE_PTRREQ1) || (x->state == STATE_PTRREQ2) || (x->state == STATE_PTRREQ3)) + +dword resrecvbuf[(MaxPacketsize + 7) >> 2]; /* MUST BE DWORD ALIGNED */ + +struct resolve *idbash[BashSize]; +struct resolve *ipbash[BashSize]; +struct resolve *hostbash[BashSize]; +struct resolve *expireresolves = NULL; +struct resolve *lastresolve = NULL; +struct logline *streamlog = NULL; +struct logline *lastlog = NULL; + +ip_t alignedip; +ip_t localhost; + +double sweeptime; + +#ifdef Debug +int debug = 1; +#else +int debug = 0; +#endif + +dword mem = 0; + +dword res_iplookupsuccess = 0; +dword res_reversesuccess = 0; +dword res_nxdomain = 0; +dword res_nserror = 0; +dword res_hostipmismatch = 0; +dword res_unknownid = 0; +dword res_resend = 0; +dword res_timeout = 0; + +dword resolvecount = 0; + +long idseed = 0xdeadbeef; +long aseed; + +struct sockaddr_in from; + +int resfd; +int fromlen = sizeof(struct sockaddr_in); + +char tempstring[16384+1+1]; +char sendstring[1024+1]; +char namestring[1024+1]; +char stackstring[1024+1]; + +char nullstring[] = ""; + +/* Code */ + +void *statmalloc(size_t size){ + void *p; + size_t mallocsize; + mem+= size; +#ifdef CorruptCheck + mallocsize = size + (sizeof(dword)) + sizeof(dword); +#else + mallocsize = size + (sizeof(dword)); +#endif + p = malloc(mallocsize); + if (!p){ + fprintf(stderr,"malloc() of %u bytes failed: %s\n",size,strerror(errno)); + exit(-1); + } + *((dword *)p) = (dword)size; +#ifdef CorruptCheck + *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 0) = 0xde; + *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 1) = 0xad; + *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 2) = 0xbe; + *(byte *)((char *)p + size + sizeof(dword) + sizeof(byte) * 3) = 0xef; +#endif + p = (void *)((dword *)p + 1); +#ifdef WipeMallocs + memset(p,0xf0,size); +#endif + return p; +} + +void statfree(void *p){ + if (p){ + if (*((dword *)p - 1) == 0){ + fprintf(stderr,"ERROR: Attempt to free pointer twice.\n"); + *(int*)0=0; + exit(-1); + } else { + if (*((dword *)p - 1) > 8192){ + fprintf(stderr,"ERROR: Corrupted free() buffer. (header)\n"); + *(int*)0=0; + exit(-1); + } +#ifdef CorruptCheck + if ((*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 0) != 0xde) || + (*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 1) != 0xad) || + (*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 2) != 0xbe) || + (*(byte *)((char *)p + (*((dword *)p - 1)) + sizeof(byte) * 3) != 0xef)){ + fprintf(stderr,"ERROR: Corrupted free() buffer. (footer)\n"); + *(int*)0=0; + exit(-1); + } +#endif + mem-= *((dword *)p - 1); +#ifdef WipeFrees + memset(p,0xfe,*((dword *)p - 1)); + *((dword *)p - 1) = 0; +#endif + free((dword *)p - 1); + } + } +} + +char *strtdiff(char *d,long signeddiff){ + dword diff; + dword seconds,minutes,hours; + long days; + if ((diff = labs(signeddiff))){ + seconds = diff % 60; diff/= 60; + minutes = diff % 60; diff/= 60; + hours = diff % 24; + days = signeddiff / (60 * 60 * 24); + if (days) + sprintf(d,"%lid",days); + else + *d = '\0'; + if (hours) + sprintf(d + strlen(d),"%luh",hours); + if (minutes) + sprintf(d + strlen(d),"%lum",minutes); + if (seconds) + sprintf(d + strlen(d),"%lus",seconds); + } else + sprintf(d,"0s"); + return d; +} + +int issetfd(fd_set *set,int fd){ + return (int)((FD_ISSET(fd,set)) && 1); +} + +void setfd(fd_set *set,int fd){ + FD_SET(fd,set); +} + +void clearfd(fd_set *set,int fd){ + FD_CLR(fd,set); +} + +void clearset(fd_set *set){ + FD_ZERO(set); +} + +char *strlongip(ip_t ip){ + struct in_addr a; + a.s_addr = ip; + return inet_ntoa(a); +} + +ip_t longipstr(char *s){ + return inet_addr(s); +} + +int dns_forward(char *name){ + struct hostent *host; + if ((host = gethostbyname(name))) + return *(int *)host->h_addr; + else + return 0; +} + +int dns_waitfd(){ + return resfd; +} + +void dns_open(){ + int option,i; + res_init(); + if (!_res.nscount){ + fprintf(stderr,"No nameservers defined.\n"); + exit(-1); + } + _res.options|= RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; + for (i = 0;i < _res.nscount;i++) + _res.nsaddr_list[i].sin_family = AF_INET; + resfd = socket(AF_INET, SOCK_DGRAM, 0); + if (resfd == -1){ + fprintf(stderr,"Unable to allocate socket for nameserver communication: %s\n", + strerror(errno)); + exit(-1); + } + option = 1; + if (setsockopt(resfd,SOL_SOCKET,SO_BROADCAST,(char *)&option,sizeof(option))){ + fprintf(stderr,"Unable to setsockopt() on nameserver communication socket: %s\n", + strerror(errno)); + exit(-1); + } + localhost = longipstr("127.0.0.1"); + aseed = time(NULL) ^ (time(NULL) << 3) ^ (dword)getpid(); + for (i = 0;i < BashSize;i++){ + idbash[i] = NULL; + hostbash[i] = NULL; + } +} + +struct resolve *allocresolve(){ + struct resolve *rp; + rp = (struct resolve *)statmalloc(sizeof(struct resolve)); + if (!rp){ + fprintf(stderr,"statmalloc() failed: %s\n",strerror(errno)); + exit(-1); + } + bzero(rp,sizeof(struct resolve)); + return rp; +} + +dword getidbash(word id){ + return (dword)BashModulo(id); +} + +dword getipbash(ip_t ip){ + return (dword)BashModulo(ip); +} + +dword gethostbash(char *host){ + dword bashvalue = 0; + for (;*host;host++){ + bashvalue^= *host; + bashvalue+= (*host >> 1) + (bashvalue >> 1); + } + return BashModulo(bashvalue); +} + +void linkresolveid(struct resolve *addrp){ + struct resolve *rp; + dword bashnum; + bashnum = getidbash(addrp->id); + rp = idbash[bashnum]; + if (rp){ + while ((rp->nextid) && (addrp->id > rp->nextid->id)) + rp = rp->nextid; + while ((rp->previousid) && (addrp->id < rp->previousid->id)) + rp = rp->previousid; + if (rp->id < addrp->id){ + addrp->previousid = rp; + addrp->nextid = rp->nextid; + if (rp->nextid) + rp->nextid->previousid = addrp; + rp->nextid = addrp; + } else { + addrp->previousid = rp->previousid; + addrp->nextid = rp; + if (rp->previousid) + rp->previousid->nextid = addrp; + rp->previousid = addrp; + } + } else + addrp->nextid = addrp->previousid = NULL; + idbash[bashnum] = addrp; +} + +void unlinkresolveid(struct resolve *rp){ + dword bashnum; + bashnum = getidbash(rp->id); + if (idbash[bashnum] == rp) + if (rp->previousid) + idbash[bashnum] = rp->previousid; + else + idbash[bashnum] = rp->nextid; + if (rp->nextid) + rp->nextid->previousid = rp->previousid; + if (rp->previousid) + rp->previousid->nextid = rp->nextid; +} + +void linkresolvehost(struct resolve *addrp){ + struct resolve *rp; + dword bashnum; + bashnum = gethostbash(addrp->hostname); + rp = hostbash[bashnum]; + if (rp){ + while ((rp->nexthost) && (strcasecmp(addrp->hostname,rp->nexthost->hostname) < 0)) + rp = rp->nexthost; + while ((rp->previoushost) && (strcasecmp(addrp->hostname,rp->previoushost->hostname) > 0)) + rp = rp->previoushost; + if (strcasecmp(addrp->hostname,rp->hostname) < 0){ + addrp->previoushost = rp; + addrp->nexthost = rp->nexthost; + if (rp->nexthost) + rp->nexthost->previoushost = addrp; + rp->nexthost = addrp; + } else { + addrp->previoushost = rp->previoushost; + addrp->nexthost = rp; + if (rp->previoushost) + rp->previoushost->nexthost = addrp; + rp->previoushost = addrp; + } + } else + addrp->nexthost = addrp->previoushost = NULL; + hostbash[bashnum] = addrp; +} + +void unlinkresolvehost(struct resolve *rp){ + dword bashnum; + bashnum = gethostbash(rp->hostname); + if (hostbash[bashnum] == rp) + if (rp->previoushost) + hostbash[bashnum] = rp->previoushost; + else + hostbash[bashnum] = rp->nexthost; + if (rp->nexthost) + rp->nexthost->previoushost = rp->previoushost; + if (rp->previoushost) + rp->previoushost->nexthost = rp->nexthost; + statfree(rp->hostname); +} + +void linkresolveip(struct resolve *addrp){ + struct resolve *rp; + dword bashnum; + bashnum = getipbash(addrp->ip); + rp = ipbash[bashnum]; + if (rp){ + while ((rp->nextip) && (addrp->ip > rp->nextip->ip)) + rp = rp->nextip; + while ((rp->previousip) && (addrp->ip < rp->previousip->ip)) + rp = rp->previousip; + if (rp->ip < addrp->ip){ + addrp->previousip = rp; + addrp->nextip = rp->nextip; + if (rp->nextip) + rp->nextip->previousip = addrp; + rp->nextip = addrp; + } else { + addrp->previousip = rp->previousip; + addrp->nextip = rp; + if (rp->previousip) + rp->previousip->nextip = addrp; + rp->previousip = addrp; + } + } else + addrp->nextip = addrp->previousip = NULL; + ipbash[bashnum] = addrp; +} + +void unlinkresolveip(struct resolve *rp){ + dword bashnum; + bashnum = getipbash(rp->ip); + if (ipbash[bashnum] == rp) + if (rp->previousip) + ipbash[bashnum] = rp->previousip; + else + ipbash[bashnum] = rp->nextip; + if (rp->nextip) + rp->nextip->previousip = rp->previousip; + if (rp->previousip) + rp->previousip->nextip = rp->nextip; +} + +void linkresolve(struct resolve *rp){ + struct resolve *irp; + if (expireresolves){ + irp = expireresolves; + while ((irp->next) && (rp->expiretime >= irp->expiretime)) irp = irp->next; + if (rp->expiretime >= irp->expiretime){ + rp->next = NULL; + rp->previous = irp; + irp->next = rp; + lastresolve = rp; + } else { + rp->previous = irp->previous; + rp->next = irp; + if (irp->previous) + irp->previous->next = rp; + else + expireresolves = rp; + irp->previous = rp; + } + } else { + rp->next = NULL; + rp->previous = NULL; + expireresolves = lastresolve = rp; + } + resolvecount++; +} + +void lastlinkresolve(struct resolve *rp){ + struct resolve *irp; + if (lastresolve){ + irp = lastresolve; + while ((irp->previous) && (rp->expiretime < irp->expiretime)) irp = irp->previous; + while ((irp->next) && (rp->expiretime >= irp->expiretime)) irp = irp->next; + if (rp->expiretime >= irp->expiretime){ + rp->next = NULL; + rp->previous = irp; + irp->next = rp; + lastresolve = rp; + } else { + rp->previous = irp->previous; + rp->next = irp; + if (irp->previous) + irp->previous->next = rp; + else + expireresolves = rp; + irp->previous = rp; + } + } else { + rp->next = NULL; + rp->previous = NULL; + expireresolves = lastresolve = rp; + } + resolvecount++; +} + +void untieresolve(struct resolve *rp){ + if (rp->previous) + rp->previous->next = rp->next; + else + expireresolves = rp->next; + if (rp->next) + rp->next->previous = rp->previous; + else + lastresolve = rp->previous; + resolvecount--; +} + +void unlinkresolve(struct resolve *rp){ + untieresolve(rp); + unlinkresolveid(rp); + unlinkresolveip(rp); + if (rp->hostname) + unlinkresolvehost(rp); +} + +struct resolve *findid(word id){ + struct resolve *rp; + int bashnum; + bashnum = getidbash(id); + rp = idbash[bashnum]; + if (rp){ + while ((rp->nextid) && (id >= rp->nextid->id)) + rp = rp->nextid; + while ((rp->previousid) && (id <= rp->previousid->id)) + rp = rp->previousid; + if (id == rp->id){ + idbash[bashnum] = rp; + return rp; + } else + return NULL; + } + return rp; /* NULL */ +} + +struct resolve *findhost(char *hostname){ + struct resolve *rp; + int bashnum; + bashnum = gethostbash(hostname); + rp = hostbash[bashnum]; + if (rp){ + while ((rp->nexthost) && (strcasecmp(hostname,rp->nexthost->hostname) >= 0)) + rp = rp->nexthost; + while ((rp->previoushost) && (strcasecmp(hostname,rp->nexthost->hostname) <= 0)) + rp = rp->previoushost; + if (strcasecmp(hostname,rp->hostname)) + return NULL; + else { + hostbash[bashnum] = rp; + return rp; + } + } + return rp; /* NULL */ +} + +struct resolve *findip(ip_t ip){ + struct resolve *rp; + dword bashnum; + bashnum = getipbash(ip); + rp = ipbash[bashnum]; + if (rp){ + while ((rp->nextip) && (ip >= rp->nextip->ip)) + rp = rp->nextip; + while ((rp->previousip) && (ip <= rp->previousip->ip)) + rp = rp->previousip; + if (ip == rp->ip){ + ipbash[bashnum] = rp; + return rp; + } else + return NULL; + } + return rp; /* NULL */ +} + +void restell(char *s){ + fputs(s,stderr); + fputs("\r",stderr); +} + +void dorequest(char *s,int type,word id){ + packetheader *hp; + int r,i; + byte buf[MaxPacketsize+1]; + r = res_mkquery(QUERY,s,C_IN,type,NULL,0,NULL,buf,MaxPacketsize); + if (r == -1){ + restell("Resolver error: Query too large."); + return; + } + hp = (packetheader *)buf; + hp->id = id; /* htons() deliberately left out (redundant) */ + for (i = 0;i < _res.nscount;i++) + (void)sendto(resfd,buf,r,0,(struct sockaddr *)&_res.nsaddr_list[i], + sizeof(struct sockaddr)); +} + +void resendrequest(struct resolve *rp,int type){ + if (type == T_A){ + dorequest(rp->hostname,type,rp->id); + if (debug){ + sprintf(tempstring,"Resolver: Sent reverse authentication request for \"%s\".", + rp->hostname); + restell(tempstring); + } + } else if (type == T_PTR){ + sprintf(tempstring,"%u.%u.%u.%u.in-addr.arpa", + ((byte *)&rp->ip)[3], + ((byte *)&rp->ip)[2], + ((byte *)&rp->ip)[1], + ((byte *)&rp->ip)[0]); + dorequest(tempstring,type,rp->id); + if (debug){ + sprintf(tempstring,"Resolver: Sent domain lookup request for \"%s\".", + strlongip(rp->ip)); + restell(tempstring); + } + } +} + +void sendrequest(struct resolve *rp,int type){ + do { + idseed = (((idseed + idseed) | (long)time(NULL)) + idseed - 0x54bad4a) ^ aseed; + aseed^= idseed; + rp->id = (word)idseed; + } while (findid(rp->id)); + linkresolveid(rp); + resendrequest(rp,type); +} + +void failrp(struct resolve *rp){ + if (rp->state == STATE_FINISHED) + return; + rp->state = STATE_FAILED; + untieresolve(rp); + if (debug) + restell("Resolver: Lookup failed.\n"); +} + +void passrp(struct resolve *rp,long ttl){ + rp->state = STATE_FINISHED; + rp->expiretime = sweeptime + (double)ttl; + untieresolve(rp); + if (debug){ + sprintf(tempstring,"Resolver: Lookup successful: %s\n",rp->hostname); + restell(tempstring); + } +} + +void parserespacket(byte *s,int l){ + struct resolve *rp; + packetheader *hp; + byte *eob; + byte *c; + long ttl; + int r,usefulanswer; + word rr,datatype,class,qdatatype,qclass; + byte rdatalength; + if (l < sizeof(packetheader)){ + restell("Resolver error: Packet smaller than standard header size."); + return; + } + if (l == sizeof(packetheader)){ + restell("Resolver error: Packet has empty body."); + return; + } + hp = (packetheader *)s; + /* Convert data to host byte order */ + /* hp->id does not need to be redundantly byte-order flipped, it is only echoed by nameserver */ + rp = findid(hp->id); + if (!rp){ + res_unknownid++; + return; + } + if ((rp->state == STATE_FINISHED) || (rp->state == STATE_FAILED)) + return; + hp->qdcount = ntohs(hp->qdcount); + hp->ancount = ntohs(hp->ancount); + hp->nscount = ntohs(hp->nscount); + hp->arcount = ntohs(hp->arcount); + if (getheader_tc(hp)){ /* Packet truncated */ + restell("Resolver error: Nameserver packet truncated."); + return; + } + if (!getheader_qr(hp)){ /* Not a reply */ + restell("Resolver error: Query packet received on nameserver communication socket."); + return; + } + if (getheader_opcode(hp)){ /* Not opcode 0 (standard query) */ + restell("Resolver error: Invalid opcode in response packet."); + return; + } + eob = s + l; + c = s + HFIXEDSZ; + switch (getheader_rcode(hp)){ + case NOERROR: + if (hp->ancount){ + if (debug){ + sprintf(tempstring,"Resolver: Received nameserver reply. (qd:%u an:%u ns:%u ar:%u)", + hp->qdcount,hp->ancount,hp->nscount,hp->arcount); + restell(tempstring); + } + if (hp->qdcount != 1){ + restell("Resolver error: Reply does not contain one query."); + return; + } + if (c > eob){ + restell("Resolver error: Reply too short."); + return; + } + switch (rp->state){ /* Construct expected query reply */ + case STATE_PTRREQ1: + case STATE_PTRREQ2: + case STATE_PTRREQ3: + sprintf(stackstring,"%u.%u.%u.%u.in-addr.arpa", + ((byte *)&rp->ip)[3], + ((byte *)&rp->ip)[2], + ((byte *)&rp->ip)[1], + ((byte *)&rp->ip)[0]); + break; + } + *namestring = '\0'; + r = dn_expand(s,s + l,c,namestring,MAXDNAME); + if (r == -1){ + restell("Resolver error: dn_expand() failed while expanding query domain."); + return; + } + namestring[strlen(stackstring)] = '\0'; + if (strcasecmp(stackstring,namestring)){ + if (debug){ + sprintf(tempstring,"Resolver: Unknown query packet dropped. (\"%s\" does not match \"%s\")", + stackstring,namestring); + restell(tempstring); + } + return; + } + if (debug){ + sprintf(tempstring,"Resolver: Queried domain name: \"%s\"",namestring); + restell(tempstring); + } + c+= r; + if (c + 4 > eob){ + restell("Resolver error: Query resource record truncated."); + return; + } + qdatatype = sucknetword(c); + qclass = sucknetword(c); + if (qclass != C_IN){ + sprintf(tempstring,"Resolver error: Received unsupported query class: %u (%s)", + qclass,qclass < ClasstypeCount ? classtypes[qclass] : + classtypes[ClasstypeCount]); + restell(tempstring); + } + switch (qdatatype){ + case T_PTR: + if (!Is_PTR(rp)) + if (debug){ + restell("Resolver warning: Ignoring response with unexpected query type \"PTR\"."); + return; + } + break; + default: + sprintf(tempstring,"Resolver error: Received unimplemented query type: %u (%s)", + qdatatype,qdatatype < ResourcetypeCount ? + resourcetypes[qdatatype] : resourcetypes[ResourcetypeCount]); + restell(tempstring); + } + for (rr = hp->ancount + hp->nscount + hp->arcount;rr;rr--){ + if (c > eob){ + restell("Resolver error: Packet does not contain all specified resouce records."); + return; + } + *namestring = '\0'; + r = dn_expand(s,s + l,c,namestring,MAXDNAME); + if (r == -1){ + restell("Resolver error: dn_expand() failed while expanding answer domain."); + return; + } + namestring[strlen(stackstring)] = '\0'; + if (strcasecmp(stackstring,namestring)) + usefulanswer = 0; + else + usefulanswer = 1; + if (debug){ + sprintf(tempstring,"Resolver: answered domain query: \"%s\"",namestring); + restell(tempstring); + } + c+= r; + if (c + 10 > eob){ + restell("Resolver error: Resource record truncated."); + return; + } + datatype = sucknetword(c); + class = sucknetword(c); + ttl = sucknetlong(c); + rdatalength = sucknetword(c); + if (class != qclass){ + sprintf(tempstring,"query class: %u (%s)",qclass,qclass < ClasstypeCount ? + classtypes[qclass] : classtypes[ClasstypeCount]); + restell(tempstring); + sprintf(tempstring,"rr class: %u (%s)",class,class < ClasstypeCount ? + classtypes[class] : classtypes[ClasstypeCount]); + restell(tempstring); + restell("Resolver error: Answered class does not match queried class."); + return; + } + if (!rdatalength){ + restell("Resolver error: Zero size rdata."); + return; + } + if (c + rdatalength > eob){ + restell("Resolver error: Specified rdata length exceeds packet size."); + return; + } + if (datatype == qdatatype){ + if (debug){ + sprintf(tempstring,"Resolver: TTL: %s",strtdiff(sendstring,ttl)); + restell(tempstring); + } + if (usefulanswer) + switch (datatype){ + case T_A: + if (rdatalength != 4){ + sprintf(tempstring,"Resolver error: Unsupported rdata format for \"A\" type. (%u bytes)", + rdatalength); + restell(tempstring); + return; + } + if (memcmp(&rp->ip,(ip_t *)c,sizeof(ip_t))){ + sprintf(tempstring,"Resolver: Reverse authentication failed: %s != ", + strlongip(rp->ip)); + memcpy(&alignedip,(ip_t *)c,sizeof(ip_t)); + strcat(tempstring,strlongip(alignedip)); + restell(tempstring); + res_hostipmismatch++; + failrp(rp); + } else { + sprintf(tempstring,"Resolver: Reverse authentication complete: %s == \"%s\".", + strlongip(rp->ip),nonull(rp->hostname)); + restell(tempstring); + res_reversesuccess++; + passrp(rp,ttl); + return; + } + break; + case T_PTR: + *namestring = '\0'; + r = dn_expand(s,s + l,c,namestring,MAXDNAME); + if (r == -1){ + restell("Resolver error: dn_expand() failed while expanding domain in rdata."); + return; + } + if (debug){ + sprintf(tempstring,"Resolver: Answered domain: \"%s\"",namestring); + restell(tempstring); + } + if (r > HostnameLength){ + restell("Resolver error: Domain name too long."); + failrp(rp); + return; + } + if (!rp->hostname){ + rp->hostname = (char *)statmalloc(strlen(namestring) + 1); + if (!rp->hostname){ + fprintf(stderr,"statmalloc() error: %s\n",strerror(errno)); + exit(-1); + } + strcpy(rp->hostname,namestring); + linkresolvehost(rp); + passrp(rp,ttl); + res_iplookupsuccess++; + } + break; + default: + sprintf(tempstring,"Resolver error: Received unimplemented data type: %u (%s)", + datatype,datatype < ResourcetypeCount ? + resourcetypes[datatype] : resourcetypes[ResourcetypeCount]); + restell(tempstring); + } + } else { + if (debug){ + sprintf(tempstring,"Resolver: Ignoring resource type %u. (%s)", + datatype,datatype < ResourcetypeCount ? + resourcetypes[datatype] : resourcetypes[ResourcetypeCount]); + restell(tempstring); + } + } + c+= rdatalength; + } + } else + restell("Resolver error: No error returned but no answers given."); + break; + case NXDOMAIN: + if (debug) + restell("Resolver: Host not found."); + res_nxdomain++; + failrp(rp); + break; + default: + sprintf(tempstring,"Resolver: Received error response %u. (%s)", + getheader_rcode(hp),getheader_rcode(hp) < ResponsecodeCount ? + responsecodes[getheader_rcode(hp)] : responsecodes[ResponsecodeCount]); + restell(tempstring); + res_nserror++; + } +} + +void dns_ack(){ + int r,i; + r = recvfrom(resfd,(byte *)resrecvbuf,MaxPacketsize,0,(struct sockaddr *)&from,&fromlen); + if (r > 0){ + /* Check to see if this server is actually one we sent to */ + if (from.sin_addr.s_addr == localhost){ + for (i = 0;i < _res.nscount;i++) + if ((_res.nsaddr_list[i].sin_addr.s_addr == from.sin_addr.s_addr) || + (!_res.nsaddr_list[i].sin_addr.s_addr)) /* 0.0.0.0 replies as 127.0.0.1 */ + break; + } else + for (i = 0;i < _res.nscount;i++) + if (_res.nsaddr_list[i].sin_addr.s_addr == from.sin_addr.s_addr) + break; + if (i == _res.nscount){ + sprintf(tempstring,"Resolver error: Received reply from unknown source: %s", + strlongip(from.sin_addr.s_addr)); + restell(tempstring); + } else + parserespacket((byte *)resrecvbuf,r); + } else { + sprintf(tempstring,"Resolver: Socket error: %s",strerror(errno)); + restell(tempstring); + } +} + +int istime(double x,double *sinterval){ + if (x) + if (x > sweeptime){ + if (*sinterval > x - sweeptime) + *sinterval = x - sweeptime; + } else + return 1; + return 0; +} + +void dns_events(double *sinterval){ + struct resolve *rp,*nextrp; + for (rp = expireresolves;(rp) && (sweeptime >= rp->expiretime);rp = nextrp){ + nextrp = rp->next; + switch (rp->state){ + case STATE_FINISHED: /* TTL has expired */ + case STATE_FAILED: /* Fake TTL has expired */ + if (debug){ + sprintf(tempstring,"Resolver: Cache record for \"%s\" (%s) has expired. (state: %u) Marked for expire at: %g, time: %g.", + nonull(rp->hostname),strlongip(rp->ip),rp->state,rp->expiretime,sweeptime); + restell(tempstring); + } + unlinkresolve(rp); + break; + case STATE_PTRREQ1: /* First T_PTR send timed out */ + resendrequest(rp,T_PTR); + restell("Resolver: Send #2 for \"PTR\" query..."); + rp->state++; + rp->expiretime = sweeptime + ResRetryDelay2; + (void)istime(rp->expiretime,sinterval); + res_resend++; + break; + case STATE_PTRREQ2: /* Second T_PTR send timed out */ + resendrequest(rp,T_PTR); + restell("Resolver: Send #3 for \"PTR\" query..."); + rp->state++; + rp->expiretime = sweeptime + ResRetryDelay3; + (void)istime(rp->expiretime,sinterval); + res_resend++; + break; + case STATE_PTRREQ3: /* Third T_PTR timed out */ + restell("Resolver: \"PTR\" query timed out."); + failrp(rp); + (void)istime(rp->expiretime,sinterval); + res_timeout++; + break; + } + } + if (expireresolves) + (void)istime(expireresolves->expiretime,sinterval); +} + +char *dns_lookup(ip_t ip){ + struct resolve *rp; + ip = htonl(ip); + if ((rp = findip(ip))){ + if ((rp->state == STATE_FINISHED) || (rp->state == STATE_FAILED)){ + if ((rp->state == STATE_FINISHED) && (rp->hostname)){ + if (debug){ + sprintf(tempstring,"Resolver: Used cached record: %s == \"%s\".\n", + strlongip(ip),rp->hostname); + restell(tempstring); + } + return rp->hostname; + } else { + if (debug){ + sprintf(tempstring,"Resolver: Used failed record: %s == ???\n", + strlongip(ip)); + restell(tempstring); + } + return strlongip(ip); + } + } + return strlongip(ip); + } + if (debug) + fprintf(stderr,"Resolver: Added to new record.\n"); + rp = allocresolve(); + rp->state = STATE_PTRREQ1; + rp->expiretime = sweeptime + ResRetryDelay1; + rp->ip = ip; + linkresolve(rp); + rp->ip = ip; + linkresolveip(rp); + sendrequest(rp,T_PTR); + return strlongip(ip); +} diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..2f4f35b --- /dev/null +++ b/dns.h @@ -0,0 +1,27 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Prototypes for dns.c */ + +void dns_open(); +int dns_waitfd(); +void dns_ack(); +void dns_events(double *sinterval); +char *dns_lookup(int address); +int dns_forward(char *name); diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..beb7450 --- /dev/null +++ b/getopt.c @@ -0,0 +1,765 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* 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 +#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 +# 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; + +#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 +#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__ */ + +/* 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; +} + +/* 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__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/getopt.h b/getopt.h new file mode 100644 index 0000000..4ac33b7 --- /dev/null +++ b/getopt.h @@ -0,0 +1,129 @@ +/* Declarations for getopt. + Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/getopt1.c b/getopt1.c new file mode 100644 index 0000000..4580211 --- /dev/null +++ b/getopt1.c @@ -0,0 +1,180 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#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 + +/* 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 +#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__. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/gtk.c b/gtk.c new file mode 100644 index 0000000..8fd7fed --- /dev/null +++ b/gtk.c @@ -0,0 +1,325 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#ifndef NO_GTK +#include +#include + +#include "net.h" +#include "dns.h" +#include "mtr-gtk.h" + +#include "img/mtr_icon.xpm" +#endif + +extern char *Hostname; +extern float WaitTime; + +void gtk_do_init(int *argc, char ***argv) { + static int done = 0; + + if(!done) { + gtk_init(argc, argv); + + done = 1; + } +} + +int gtk_detect(int *argc, char ***argv) { + if(getenv("DISPLAY") != NULL) { + gtk_do_init(argc, argv); + return TRUE; + } else { + return FALSE; + } +} + +gint Window_destroy(GtkWidget *Window, gpointer data) { + gtk_main_quit(); + + return FALSE; +} + +gint Restart_clicked(GtkWidget *Button, gpointer data) { + net_reset(); + gtk_redraw(); + + return FALSE; +} + +gint Host_activate(GtkWidget *Entry, gpointer data) { + int addr; + + addr = dns_forward(gtk_entry_get_text(GTK_ENTRY(Entry))); + if(addr) + net_reopen(addr); + + return FALSE; +} + +GdkPixmap *gtk_load_pixmap(char **pixmap) { + return gdk_pixmap_colormap_create_from_xpm_d(NULL, + gdk_colormap_get_system(), + NULL, NULL, pixmap); +} + +void Toolbar_fill(GtkWidget *Toolbar) { + GtkWidget *Button; + GtkWidget *Label; + GtkWidget *Entry; + + Button = gtk_button_new_with_label("Quit"); + gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0); + gtk_signal_connect(GTK_OBJECT(Button), "clicked", + GTK_SIGNAL_FUNC(Window_destroy), NULL); + gtk_widget_show(Button); + + Button = gtk_button_new_with_label("Restart"); + gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0); + gtk_signal_connect(GTK_OBJECT(Button), "clicked", + GTK_SIGNAL_FUNC(Restart_clicked), NULL); + gtk_widget_show(Button); + + Label = gtk_label_new("Hostname"); + gtk_box_pack_start(GTK_BOX(Toolbar), Label, FALSE, FALSE, 0); + gtk_widget_show(Label); + + Entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(Entry), Hostname); + gtk_signal_connect(GTK_OBJECT(Entry), "activate", + GTK_SIGNAL_FUNC(Host_activate), NULL); + gtk_box_pack_start(GTK_BOX(Toolbar), Entry, TRUE, TRUE, 0); + gtk_widget_show(Entry); +} + +char *Report_Text[] = { "Hostname", "Loss", "Rcv", "Snt", "Best", "Avg", "Worst", NULL }; +int Report_Positions[] = { 10, 240, 280, 320, 360, 400, 440, 0 }; +GtkWidget *Report; +GtkWidget *ReportBody; + +GtkWidget *GetRow(int index) { + int addr; + char str[256], *name; + GtkWidget *Row, *Label; + + Row = gtk_fixed_new(); + + addr = net_addr(index); + name = "???"; + if(addr != 0) { + name = dns_lookup(addr); + if(!name) { + sprintf(str, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + name = str; + } + } + + Label = gtk_label_new(name); + gtk_fixed_put(GTK_FIXED(Row), Label, Report_Positions[0], 0); + gtk_widget_show(Label); + + return Row; +} + +GtkWidget *Scrollarea_create() { + GtkWidget *List; + int count; + + for(count = 0; Report_Positions[count]; count++); + + List = GTK_WIDGET(gtk_clist_new_with_titles(count, Report_Text)); + gtk_clist_set_policy(GTK_CLIST(List), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + for(count = 0; Report_Positions[count + 1]; count++) { + gtk_clist_set_column_width(GTK_CLIST(List), count, + Report_Positions[count + 1] - + Report_Positions[count]); + } + gtk_clist_set_column_width(GTK_CLIST(List), count, 0); + for(count = 1; Report_Positions[count]; count++) { + gtk_clist_set_column_justification(GTK_CLIST(List), count, GTK_JUSTIFY_RIGHT); + } + + ReportBody = List; + return List; +} + +void gtk_add_row(GtkWidget *List) { + int at; + GtkWidget *Row, *Label; + + Row = gtk_fixed_new(); + + for(at = 0; Report_Positions[at] != 0; at++) { + Label = gtk_label_new("-"); + if(at) { + gtk_widget_set_usize(Label, 40, 0); + gtk_label_set_justify(GTK_LABEL(Label), GTK_JUSTIFY_RIGHT); + } + gtk_fixed_put(GTK_FIXED(Row), Label, Report_Positions[at], 0); + gtk_widget_show(Label); + } + + gtk_box_pack_start(GTK_BOX(List), Row, FALSE, FALSE, 0); + gtk_widget_show(Row); +} + +void gtk_set_field(GtkCList *List, int row, int ix, char *str) { + gtk_clist_set_text(List, row, ix, str); +} + +void gtk_set_field_num(GtkCList *List, int row, int ix, char *format, int num) { + char str[32]; + + sprintf(str, format, num); + gtk_set_field(List, row, ix, str); +} + +void gtk_update_row(GtkCList *List, int row) { + int addr; + char str[256], *name; + + addr = net_addr(row); + name = "???"; + if(addr != 0) { + name = dns_lookup(addr); + if(!name) { + sprintf(str, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + name = str; + } + } + gtk_set_field(List, row, 0, name); + + gtk_set_field_num(List, row, 1, "%d%%", net_percent(row)); + gtk_set_field_num(List, row, 2, "%d", net_returned(row)); + gtk_set_field_num(List, row, 3, "%d", net_xmit(row)); + + gtk_set_field_num(List, row, 4, "%d", net_best(row)); + gtk_set_field_num(List, row, 5, "%d", net_avg(row)); + gtk_set_field_num(List, row, 6, "%d", net_worst(row)); + +} + +void gtk_redraw() { + int at; + int max = net_max(); + + gtk_clist_freeze(GTK_CLIST(ReportBody)); + + while(GTK_CLIST(ReportBody)->rows < max) { + gtk_clist_append(GTK_CLIST(ReportBody), Report_Text); + } + + while(GTK_CLIST(ReportBody)->rows > max) { + gtk_clist_remove(GTK_CLIST(ReportBody), GTK_CLIST(ReportBody)->rows - 1); + } + + for(at = 0; at < max; at++) { + gtk_update_row(GTK_CLIST(ReportBody), at); + } + + gtk_clist_thaw(GTK_CLIST(ReportBody)); +} + +void Window_fill(GtkWidget *Window) { + GtkWidget *VBox; + GtkWidget *Toolbar; + GtkWidget *List; + + gtk_window_set_title(GTK_WINDOW(Window), "Matt's traceroute [v" VERSION "]"); + gtk_widget_set_usize(Window, 540, 400); + gtk_container_border_width(GTK_CONTAINER(Window), 10); + VBox = gtk_vbox_new(FALSE, 10); + + Toolbar = gtk_hbox_new(FALSE, 10); + Toolbar_fill(Toolbar); + gtk_box_pack_start(GTK_BOX(VBox), Toolbar, FALSE, FALSE, 0); + gtk_widget_show(Toolbar); + + List = Scrollarea_create(); + gtk_box_pack_start(GTK_BOX(VBox), List, TRUE, TRUE, 0); + gtk_widget_show(List); + + gtk_container_add(GTK_CONTAINER(Window), VBox); + gtk_widget_show(VBox); +} + +void gtk_open() { + GtkWidget *Window; + GdkPixmap *icon; + + int argc; + char *args[2]; + char **argv; + argc = 1; + argv = args; + argv[0] = ""; + argv[1] = NULL; + gtk_do_init(&argc, &argv); + + Window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + Window_fill(Window); + + gtk_signal_connect_object(GTK_OBJECT(Window), "delete_event", + GTK_SIGNAL_FUNC(gtk_widget_destroy), + GTK_OBJECT(Window)); + gtk_signal_connect(GTK_OBJECT(Window), "destroy", + GTK_SIGNAL_FUNC(Window_destroy), NULL); + + icon = gtk_load_pixmap(mtr_icon); + gtk_widget_show(Window); + gdk_window_set_icon(Window->window, NULL, icon, NULL); + gdk_window_set_icon_name(Window->window, "mtr"); +} + +void gtk_close() { +} + +int gtk_keyaction() { + return 0; +} + +gint gtk_ping(gpointer data) { + gtk_redraw(); + net_send_batch(); + + return TRUE; +} + +void gtk_net_data(gpointer data, gint fd, GdkInputCondition cond) { + net_process_return(); +} + +void gtk_dns_data(gpointer data, gint fd, GdkInputCondition cond) { + dns_ack(); + + gtk_redraw(); +} + +void gtk_loop() { + gtk_timeout_add((int)(1000.0 * WaitTime), gtk_ping, NULL); + gdk_input_add(net_waitfd(), GDK_INPUT_READ, gtk_net_data, NULL); + gdk_input_add(dns_waitfd(), GDK_INPUT_READ, gtk_dns_data, NULL); + + gtk_main(); +} diff --git a/img/Makefile.am b/img/Makefile.am new file mode 100644 index 0000000..b8679df --- /dev/null +++ b/img/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = mtr_icon.xpm diff --git a/img/mtr_icon.xpm b/img/mtr_icon.xpm new file mode 100644 index 0000000..5c809b7 --- /dev/null +++ b/img/mtr_icon.xpm @@ -0,0 +1,182 @@ +/* XPM */ +static char * mtr_icon[] = { +"48 48 131 2", +" c #000000", +". c #020204", +"+ c #3E7E3C", +"@ c #62BA64", +"# c #4E9E4C", +"$ c #628664", +"% c #6EDA6C", +"& c #7A9E7C", +"* c #468E44", +"= c #6ACA6C", +"- c #224224", +"; c #56AE54", +"> c #326234", +", c #567E54", +"' c #7ABA7C", +") c #3A723C", +"! c #629E64", +"~ c #428644", +"{ c #6E926C", +"] c #7AEA7C", +"^ c #4A964C", +"/ c #62C264", +"( c #52A654", +"_ c #4A7E4C", +": c #82AE84", +"< c #366A34", +"[ c #5AB65C", +"} c #4A764C", +"| c #6AD26C", +"1 c #8EC68C", +"2 c #7A9A7C", +"3 c #7AE27C", +"4 c #82A684", +"5 c #568E54", +"6 c #568654", +"7 c #3E7A3C", +"8 c #6AC26C", +"9 c #3E6A3C", +"0 c #468244", +"a c #56A254", +"b c #4E8E4C", +"c c #5EAE5C", +"d c #86C684", +"e c #427244", +"f c #62A664", +"g c #769A74", +"h c #82F284", +"i c #5A9A5C", +"j c #5AA65C", +"k c #428244", +"l c #66BE64", +"m c #6A8E6C", +"n c #76DA74", +"o c #7E9E7C", +"p c #2A522C", +"q c #5E825C", +"r c #82BE84", +"s c #669E64", +"t c #6E966C", +"u c #529A54", +"v c #62C664", +"w c #4A824C", +"x c #366E34", +"y c #72D674", +"z c #427E3C", +"A c #5EBE5C", +"B c #52A254", +"C c #628A64", +"D c #4A924C", +"E c #6ACE6C", +"F c #5AB25C", +"G c #326634", +"H c #568254", +"I c #7ABE7C", +"J c #3A763C", +"K c #468A44", +"L c #7EEE7C", +"M c #4E9A4C", +"N c #56AA54", +"O c #86B684", +"P c #62B664", +"Q c #162A14", +"R c #529E54", +"S c #72DA74", +"T c #76A274", +"U c #4A8E4C", +"V c #264A24", +"W c #5AAE5C", +"X c #5EA25C", +"Y c #468644", +"Z c #729274", +"` c #7EEA7C", +" . c #4E964C", +".. c #66C264", +"+. c #56A654", +"@. c #4E7E4C", +"#. c #8AB28C", +"$. c #3A6A3C", +"%. c #5EB65C", +"&. c #527A54", +"*. c #6ED26C", +"=. c #92C694", +"-. c #7E9A7C", +";. c #7AE67C", +">. c #86AA84", +",. c #5E865C", +"'. c #6AC66C", +"). c #3E6E3C", +"!. c #5AA25C", +"~. c #4E924C", +"{. c #5EB25C", +"]. c #467244", +"^. c #86F284", +"/. c #5AAA5C", +"(. c #76DE74", +"_. c #829E84", +":. c #325E34", +"<. c #66A264", +"[. c #729674", +"}. c #66C664", +"|. c #4E824C", +"1. c #3A6E3C", +"2. c #76D674", +"3. c #427E44", +"4. c #62BE64", +"5. c #668A64", +"6. c #6ECE6C", +"7. c #366634", +"8. c #5A825C", +"9. c #7EBE7C", +"0. c #3E763C", +"G 7.G G G G G 7.G < z . .* k 1.G G G G G G 0.K x 7.7.7.7.7.< J Y D M D ) 7.7.7.7.7.7.< 7.7.7.7.", +"7.G 7.> > > 7.G ) D M K 7 < > > > > > > > > + * < G G G G G G 7.G x k * M K x G G G G G G G G G ", +"G G 7.G > > < ~ M D + G > > G > > 7.7.7.7.> 7 D x 7.7.7.7.7.G 7.7.G < ) ~ D M 7 7.7.7.7.7.7.7.7.", +"7.7.G > 7.< * M ~ x > G > G > > 7.> > > > G 7 * 1.G G G G G 7.G G 7.G G G J * M ~ < G G G G G G ", +"> G G > x D M k < G > G > > G > > G G G > 7.7 * x 7.7.7.7.G 7.7.G 7.7.7.7.G x ~ # Y < 7.7.7.7.7.", +"G > > x ^ .0.G G G 7.> > > > > G > > > 7.> 7 U 1.G G G G 7.G G 7.G G G G 7.G < k # K < G G G G ", +"G G < .^ J G G 7.G 7.> G > > > > 7.7.> 7.7.7 * x 7.7.7.G 7.7.G 7.7.7.7.G 7.G G 7.+ B ~ < 7.7.7.", +"> < D M 7 G > > > G > G > > G > 7.> > G > > + U 1.G G G 7.G G 7.G G G G 7.G 7.7.G < k B + < G G ", +"G k # k < 7.7.G > G > > G > > > > G x k .R F A N R U 7 x 7.G 7.7.7.7.G 7.G G 7.G G < K # J $.8.", +") ( K < > > G G 7.G G > > > G G + M +.B M D B W ^ ^ # ( +.^ ) G G G G 7.G 7.7.G 7.7.7.x u ! & o ", +"^ .x > > > > G 7.7.G > > > 0. .+.M K + ) < + D ) < J k * R +.D x 7.G 7.G G 7.G G G 7.e T =.& H ", +"+.+ < G G G 7.G > G G > G ~ ( B ~ ) G > G > 7 U < 7.7.G < J * B B 7 7.G 7.7.G 7.7.9 5.o Z s a ) ", +"D x > > > > > G > G G < K ( D 0.> G 7.> G 7.+ * < G 7.7.G G < + # ( + G G 7.G $.,.2 [.$ ).0.( k ", +"7 7.G G G G 7.G G > 7.K +.K x > G > > G > > 7 D < 7.G G 7.7.G 7.) M +.7 7.< &.[.2 C &.7.7.7.* B ", +"x 7.> > > > > 7.7.G k +.* < > > > G > 7.G G 7 * < 7.7.G G 7.G G G ) M B &.[.o m &.7.G 7.G 7.) %.", +"7.G 7.7.7.G 7.G G x B ^ ) > G > 7.> 7.> > > + D 7.G G 7.7.G 7.7.7.< w r #.{ , 9 7.7.7.G 7.G < ( ", +"> G G > 7.G > 7.7.* ( 7 G > > > > > G 7.1.z B F D 7 < G 7.G G G $.q o : I _ < > G G 7.G 7.G 7.~ ", +"7.7.> G > 7.7.> x +.* 7.> > G > G G < k # # +.[ B N ( k G 7.$.&.2 2 C |.B D < 7.7.7.G 7.G 7.G < ", +"> 7.> G > G G > ~ +.+ > > > > > > < K R K + K M 3.K B W K } Z -.m &.< < Y W ) G G 7.G 7.G 7.G 7.", +"> G G > > > 7.G # ^ ) > G > G > 7.~ # + < > 7 * < G J j d 4 { , $.G 7.G 7 F + 7.7.G 7.G 7.G 7.G ", +"> > > G G > 7.7.N * 7.> > > > > J D Y > G 7.3.D < ).$ 4 1 ' e 7.7.G G G ) # D < G G 7.G 7.G 7.7.", +"> 7.G > > G > x +.Y G > G > G > Y ~.x G > > 7 ~.8.-.2 5.i A 7 G G 7.7.7.< D # x 7.7.G 7.G 7.G G ", +"< < x x < x x + ; * x x x x x ) ^ .1.) 1.) 5 r o t 6 0.K A D J ) ) ) J 7 .F k 7 7 7 7 J 0.0.x ", +"D D * * D U U # A ( * * * K * * N W U U * K X 9.<.D K K R ..N U * * * * U ( A D K K K K U * K ) ", +"7 z z 7 7 z 7 K {.M 7 7 7 0.0.7 # B J 7 7 7 U R 0.7 7 7 D / D 0.0.7 0.J 7 .F + 0.J 0.J 0.J 0.< ", +"G > > > G > G < N K 7.> > G G > ~ D 1.G > > 3.U G G G G * A J G G G G 7.x D # x G 7.G 7.G 7.G G ", +"G G G > G > G < ( * 7.> > > > > J ^ K G G > + D 7.7.7.7 N ( < 7.7.7.G G ) B * < G 7.G 7.G 7.7.G ", +"G G > > > 7.G G # .) G > > G > > k u 0 7.G z U G G 7 ( F + < G G G 7.7.7 W + < 7.G 7.G 7.G G 7.", +"> 7.G 7.> > > 7.~ B + > > > G G > x ~ # .+ ~ .7 D ; ; k G 7.7.7.G G G K W x G 7.G 7.G 7.7.G 7.", +"7.G 7.> G 7.7.G < ( D < G > > > > > < z D # %.A +.B .7 < K = G G 7.7.J B D < 7.G 7.G 7.G G 7.x ", +"G G G > > > > G G K ( 7 > > > > > G > G 1.+ R N K 7 x G ` (./.7.G G < K ; J G 7.G 7.G 7.7.G 7.Y ", +"7.G G > > G G G G x B M ) > > G G > G G > > + D G G 7.7.;.E 4.G 7.7.7 N K < 7.G 7.G 7.G G 7.< ; ", +"1.> G G > > > ` ] %.+ L 3 (.S S +.> /.L ;.(.S y 7.G 6.3 6.= = y ;...B # ` ] @ *.;.S B 7.G G J A ", +"z G > > G 7.7.% = '.n ~.K %.= = = y *.u .4.= = {.7.c /.= = ..u M M B J y = E 6.!.b z G 7.< D W ", +"M ) G > > G G y = ..7.. . +.@ '.= @ V . . 1.}.= {.. G 7.6.= B . . . . 7.y = ..> . . . . G ) [ D ", +"[ + G G G > 7.S = a . . . . 4.E = z . . . G y = @ . . G (.6.a . . . . G y = .. . . . . < ^ W J ", +"N B ) G G G 7.y = .. . . 7.}.6.'.0.. . . G (.= %.. . ~ y = B . . . 7.G *.'.0 . . . G 7.7 [ K < ", +"+ [ D < > > G % = R . . > > }.6.}.0.. . +.+.3 = {.. . B (.E W . . G G 7.y = K . . G 7.x +.( J G ", +"< D {.Y 7.> 7.y = u . . > > ..6.'.0.. . K U (.= %.. . + y = +.. . 7.G 7.y }.Y . . G < ^ F z 7.7.", +"> x B ; + G > y = ( . . G > }.6.}.0.. . > G (.= {.. . G (.= {.. . G 7.G y = k . . < * [ U < G G ", +"> G J +.N + G S '.R . . > G }.6.}.0.. . G G (.= [ . . G *.= }.J . G 7.G *.= ~ . . ~ %.^ x 7.7.7.", +"G > G 7 +.N z y E B . . G > ..6.'.0.. . > > 3 = {.. . 7.'.= = = (.3 p 7.y '.0 . . {.# ) 7.G G G ", +"G > G G 7 ( ; @ N ~.. . > > 4.W +.1.. . G 7.= /.B . . G > {.l 4.%.R G 7.@ /.+ . . M ) G 7.7.7.7.", +"> 7.> > G 0.# %.^ . . . > > > > > . . > > > + D . . . 7.7.7.- - Q . . G < . . . . ) G 7.G G G G ", +"G G 7.> 7.7.) ^ F . . < G > > > G . . 7.G > 3.D 7.. G G G 7.7.. . . . x D F . . < 7.G 7.7.7.7.7.", +"> 7.G G > G G < ~ ( [ B + < > G > > > > > 7.+ U G 7.7.7.7.G G 7.G x K W [ R + < G G 7.G G G G G ", +"> > > G > > > > 7.J D +.[ ; K x G 7.G 7.> 7.) z G G G G 7.7.< 0. .F F B ~ 1.G 7.7.G 7.7.7.7.7.7.", +"G 7.7.> > G 7.G > 7.< M E | +.7 > > > > G > x ) 7.> 7.7.G G < k F % / ~ G G 7.G G 7.G G G G G G "}; diff --git a/mtr-curses.h b/mtr-curses.h new file mode 100644 index 0000000..c94880f --- /dev/null +++ b/mtr-curses.h @@ -0,0 +1,24 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Prototypes for curses.c */ +void mtr_curses_open(); +void mtr_curses_close(); +void mtr_curses_redraw(); +int mtr_curses_keyaction(); diff --git a/mtr-gtk.h b/mtr-gtk.h new file mode 100644 index 0000000..fe87bb3 --- /dev/null +++ b/mtr-gtk.h @@ -0,0 +1,26 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Prototypes for gtk.c */ +int gtk_detect(int *argc, char ***argv); +void gtk_open(); +void gtk_close(); +void gtk_redraw(); +int gtk_keyaction(); +void gtk_loop(); diff --git a/mtr.8 b/mtr.8 new file mode 100644 index 0000000..616cfd7 --- /dev/null +++ b/mtr.8 @@ -0,0 +1,175 @@ +.TH MTR 8 "December 28, 1997" "mtr" "mtr" + + +.SH NAME +mtr \- a network diagnostic tool + + +.SH SYNOPSIS +.B mtr +[\c +.B \-hvrctgi\c +] +[\c +.B \-\-help\c +] +[\c +.B \-\-version\c +] +[\c +.B \-\-report\c +] +[\c +.B \-\-report\-cycles\ COUNT\c +] +[\c +.B \-\-curses\c +] +[\c +.B \-\-gtk\c +] +[\c +.B \-\-interval\ SECONDS\c +] +.B HOSTNAME + + +.SH DESCRIPTION + +.B mtr +combines the functionaly of the +.B traceroute +and +.B ping +programs in a single network diagnostic tool. + +.PP +As +.B mtr +starts, it investigates the network connection between the host +.B mtr +runs on and +.BR HOSTNAME . +After it determines the address of each network hop between the +machines, it sends a sequence ICMP ECHO requests to each one to +determine the quality of the link to each machine. As it does this, +it prints running statistics about each machine. + + +.SH OPTIONS + +.TP +.B \-h +.TP +.B \-\-help +.br +Print the summary of command line argument options. + +.TP +.B \-v +.TP +.B \-\-version +.br +Print the installed version of mtr. + +.TP +.B \-r +.TP +.B \-\-report +.br +This option puts +.B mtr +into +.B report +mode. When in this mode, +.B mtr +will run for the number of cycles specified by the +.B \-c +option, and then print statistics and exit. +.TP +\c +This mode is useful for generating statistics about network quality. +Note that each running instance of +.B mtr +generates a significant amount of network traffic. Using +.B mtr +at to measure the quality of your network may result in decreased +network performance. + +.TP +.B \-c\ COUNT +.TP +.B \-\-report\-cycles\ COUNT +Use this option to set the number of pings sent to determine +both the machines on the network and the reliability of +those machines. Each cycle lasts one second. This option +is only useful with the +.B -r +option. + +.TP +.B \-t +.TP +.B \-\-curses +.br +Use this option to force +.B mtr +to use the curses based terminal +interface (if available). + +.TP +.B \-g +.TP +.B \-\-gtk +.br +Use this option to force +.B mtr +to use the GTK+ based X11 window interface (if available). +GTK+ must have been available on the system when +.B mtr +was built for this to work. See the GTK+ web page at +.B http://www.gimp.org/gtk/ +for more information about GTK+. + +.TP +.B \-i\ SECONDS +.TP +.B \-\-interval\ SECONDS +.br +Use this option to specify the positive number of seconds between ICMP +ECHO requests. The default value for this parameter is one second. + +.SH BUGS + +Some modern routers give a lower priority to ICMP ECHO packets than +to other network traffic. Consequently, the reliability of these +routers reported by +.B mtr +will be significantly lower than the actual reliability of +these routers. + + +.SH CONTACT INFORMATION + +.PP +For the latest version, see the mtr web page at +.BR http://www.mkimball.org/mtr.html . + +.PP +Subscribe to the mtr mailing list. All mtr related announcements +are posted to the mtr mailing list. To subscribe, send email to +.B majordomo@lists.xmission.com +with +.B subscribe\ mtr +in the body of the message. To send a message to the mailing list, mail to +.BR mtr@lists.xmission.com . + +.PP +Bug reports and feature requests should be sent to the mtr +mailing list. + + +.SH "SEE ALSO" + +traceroute(8), +ping(8). diff --git a/mtr.c b/mtr.c new file mode 100644 index 0000000..cb1dc8e --- /dev/null +++ b/mtr.c @@ -0,0 +1,165 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include + +#include "mtr-curses.h" +#include "getopt.h" +#include "display.h" +#include "dns.h" +#include "report.h" +#include "net.h" + +int DisplayMode; +int Interactive = 1; +int PrintVersion = 0; +int PrintHelp = 0; +int MaxPing = 16; +float WaitTime = 1.0; +char *Hostname = NULL; + +void parse_arg(int argc, char **argv) { + int opt; + static struct option long_options[] = { + { "version", 0, 0, 'v' }, + { "help", 0, 0, 'h' }, + { "report", 0, 0, 'r' }, + { "report-cycles", 1, 0, 'c' }, + { "curses", 0, 0, 't' }, + { "gtk", 0, 0, 'g' }, + { "interval", 1, 0, 'i' }, + { 0, 0, 0, 0 } + }; + + opt = 0; + while(1) { + opt = getopt_long(argc, argv, "hvrc:tli:", long_options, NULL); + if(opt == -1) + break; + + switch(opt) { + case 'v': + PrintVersion = 1; + break; + case 'h': + PrintHelp = 1; + break; + case 'r': + DisplayMode = DisplayReport; + break; + case 'c': + MaxPing = atoi(optarg); + break; + case 't': + DisplayMode = DisplayCurses; + break; + case 'g': + DisplayMode = DisplayGTK; + break; + case 'i': + WaitTime = atof(optarg); + if (WaitTime <= 0.0) { + fprintf (stderr, "mtr: wait time must be positive\n"); + exit (-1); + } + break; + } + } + + if(DisplayMode == DisplayReport) + Interactive = 0; + + if(optind != argc - 1) + return; + + Hostname = argv[optind]; + +} + +int main(int argc, char **argv) { + int traddr; + struct hostent *host; + + /* Get the raw sockets first thing, so we can drop to user euid immediately */ + if(net_preopen() != 0) { + printf("mtr: Unable to get raw socket. (Executable not suid?)\n"); + exit(0); + } + + /* Now drop to user permissions */ + if(seteuid(getuid())) { + printf("mtr: Unable to drop permissions.\n"); + exit(0); + } + + /* Double check, just in case */ + if(geteuid() != getuid()) { + printf("mtr: Unable to drop permissions.\n"); + exit(0); + } + + display_detect(&argc, &argv); + parse_arg(argc, argv); + + if(PrintVersion) { + printf("mtr " VERSION "\n"); + exit(0); + } + + if(Hostname == NULL || PrintHelp) { + printf("usage: %s [-hvrctli] [--help] [--version] [--report]\n" + "\t\t[--report-cycles=COUNT] [--curses] [--gtk]\n" + "\t\t[--interval=SECONDS] HOSTNAME\n", argv[0]); + exit(0); + } + + host = gethostbyname(Hostname); + if(host == NULL) { +#ifndef NO_HERROR + herror("mtr"); +#else + printf("mtr: error looking up \"%s\"\n", Hostname); +#endif + exit(0); + } + + traddr = *(int *)host->h_addr; + + if(net_open(traddr) != 0) { + printf("mtr: Unable to get raw socket. (Executable not suid?)\n"); + exit(0); + } + + display_open(); + dns_open(); + + display_loop(); + + net_end_transit(); + display_close(); + net_close(); + + return 0; +} + + diff --git a/net.c b/net.c new file mode 100644 index 0000000..1dc7372 --- /dev/null +++ b/net.c @@ -0,0 +1,428 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +#if defined(HAVE_SYS_XTI_H) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "net.h" + +#define MaxHost 256 +#define MaxTransit 4 + +/* We can't rely on header files to provide this information, because + the fields have different names between, for instance, Linux and + Solaris */ +struct ICMPHeader { + unsigned char type; + unsigned char code; + unsigned short checksum; + unsigned short id; + unsigned short sequence; +}; + +/* Structure of an IP header. */ +struct IPHeader { + uint8 version; + uint8 tos; + uint16 len; + uint16 id; + uint16 frag; + uint8 ttl; + uint8 protocol; + uint16 check; + uint32 saddr; + uint32 daddr; +}; + +#define ICMP_ECHO 8 +#define ICMP_ECHOREPLY 0 +#define ICMP_TIME_EXCEEDED 11 + +#ifndef SOL_IP +#define SOL_IP 0 +#endif + +struct packetdata { + int index; + int ttl; + int sec; + int msec; +}; + +struct nethost { + int addr; + int xmit; + int returned; + int total; + int best; + int worst; + int transit; +}; + +static struct nethost host[MaxHost]; +static struct timeval reset = { 0, 0 }; + +int sendsock; +int recvsock; +struct sockaddr_in remoteaddress; + +int checksum(void *data, int sz) { + unsigned short *ch; + unsigned int sum; + + sum = 0; + ch = data; + sz = sz / 2; + while(sz--) { + sum += *(ch++); + } + + sum = (sum >> 16) + (sum & 0xffff); + + return (~sum & 0xffff); +} + +void net_send_ping(int index) { + char packet[sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + + sizeof(struct packetdata)]; + struct IPHeader *ip; + struct ICMPHeader *icmp; + struct packetdata *data; + int packetsize = sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata); + struct sockaddr_in addr; + struct timeval now; + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = host[index].addr; + host[index].xmit++; + host[index].transit = 1; + + memset(packet, 0, packetsize); + + ip = (struct IPHeader *)packet; + icmp = (struct ICMPHeader *)(packet + sizeof(struct IPHeader)); + data = (struct packetdata *)(packet + sizeof(struct IPHeader) + sizeof(struct ICMPHeader)); + + ip->version = 0x45; + ip->tos = 0; + ip->len = packetsize; + ip->id = 0; + ip->frag = 0; + ip->ttl = 127; + ip->protocol = IPPROTO_ICMP; + ip->saddr = 0; + ip->daddr = host[index].addr; + + icmp->type = ICMP_ECHO; + icmp->id = getpid(); + icmp->sequence = 0; + + data->ttl = 0; + data->index = index; + + gettimeofday(&now, NULL); + data->sec = now.tv_sec; + data->msec = now.tv_usec / 1000; + + icmp->checksum = checksum(icmp, packetsize - sizeof(struct IPHeader)); + ip->check = checksum(ip, packetsize); + + sendto(sendsock, packet, packetsize, 0, + (struct sockaddr *)&addr, sizeof(addr)); +} + +/* Attempt to find the host at a particular number of hops away */ +void net_send_query(int hops) { + char packet[sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata)]; + struct IPHeader *ip; + struct ICMPHeader *icmp; + struct packetdata *data; + int packetsize = sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata); + + memset(packet, 0, packetsize); + + ip = (struct IPHeader *)packet; + icmp = (struct ICMPHeader *)(packet + sizeof(struct IPHeader)); + data = (struct packetdata *)(packet + sizeof(struct IPHeader) + sizeof(struct ICMPHeader)); + + ip->version = 0x45; + ip->tos = 0; + ip->len = packetsize; + ip->id = 0; + ip->frag = 0; + ip->ttl = hops; + ip->protocol = IPPROTO_ICMP; + ip->saddr = 0; + ip->daddr = remoteaddress.sin_addr.s_addr; + + icmp->type = ICMP_ECHO; + icmp->id = getpid(); + icmp->sequence = hops; + + data->ttl = hops; + data->index = -1; + + icmp->checksum = checksum(icmp, packetsize - sizeof(struct IPHeader)); + ip->check = checksum(ip, packetsize); + + sendto(sendsock, packet, packetsize, 0, + (struct sockaddr *)&remoteaddress, sizeof(remoteaddress)); +} + +void net_process_ping(struct packetdata *data, struct sockaddr_in *addr) { + int at; + struct timeval now; + int totmsec; + int msec; + + if(data->index >= 0) { + gettimeofday(&now, NULL); + + if(data->sec < reset.tv_sec + || (data->sec == reset.tv_sec && (1000*data->msec) < reset.tv_usec)) + /* discard this data point, stats were reset after it was generated */ + return; + + totmsec = (now.tv_sec - data->sec) * 1000; + msec = now.tv_usec / 1000 - data->msec; + if(msec >= 0) + totmsec += msec; + else + totmsec = totmsec - 1000 + 1000 - data->msec + now.tv_usec / 1000; + + if(host[data->index].returned <= 0) { + host[data->index].best = host[data->index].worst = totmsec; + } + + if(totmsec < host[data->index].best) + host[data->index].best = totmsec; + + if(totmsec > host[data->index].worst) + host[data->index].worst = totmsec; + + host[data->index].total += totmsec; + host[data->index].returned++; + host[data->index].transit = 0; + } else { + at = data->ttl - 1; + if(at < 0 || at > MaxHost) + return; + + host[at].addr = addr->sin_addr.s_addr; + } +} + +void net_process_return() { + char packet[2048]; + struct sockaddr_in fromaddr; + int fromaddrsize; + int num; + int at; + struct ICMPHeader *header; + + fromaddrsize = sizeof(fromaddr); + num = recvfrom(recvsock, packet, 2048, 0, + (struct sockaddr *)&fromaddr, &fromaddrsize); + + if(num < sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + sizeof(struct packetdata)) + return; + + header = (struct ICMPHeader *)(packet + sizeof(struct IPHeader)); + if(header->type == ICMP_ECHOREPLY) { + if(header->id != getpid()) + return; + + net_process_ping((struct packetdata *)(packet + sizeof(struct IPHeader) + + sizeof(struct ICMPHeader)), + &fromaddr); + } else if(header->type == ICMP_TIME_EXCEEDED) { + if(num < sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + + sizeof(struct IPHeader) + sizeof(struct ICMPHeader)) + return; + + header = (struct ICMPHeader *)(packet + sizeof(struct IPHeader) + + sizeof(struct ICMPHeader) + sizeof(struct IPHeader)); + if(header->id != getpid()) + return; + + at = header->sequence - 1; + if(at < 0 || at > MaxHost) + return; + + host[at].addr = fromaddr.sin_addr.s_addr; + } +} + +int net_addr(int at) { + return ntohl(host[at].addr); +} + +int net_percent(int at) { + if(host[at].xmit == 0) + return 0; + + return 100 - (100 * (host[at].returned + host[at].transit) / host[at].xmit); +} + +int net_best(int at) { + return host[at].best; +} + +int net_worst(int at) { + return host[at].worst; +} + +int net_avg(int at) { + if(host[at].returned == 0) + return 0; + + return host[at].total / host[at].returned; +} + +int net_max() { + int at; + int max; + + max = 0; + for(at = 0; at < MaxHost; at++) { + if(host[at].addr == remoteaddress.sin_addr.s_addr) { + return at + 1; + } else if(host[at].addr != 0) { + max = at + 2; + } + } + + return max; +} + + +/* Added by Brian Casey December 1997 bcasey@imagiware.com*/ +int net_returned(int at) { + return host[at].returned; +} +int net_xmit(int at) { + return host[at].xmit; +} +int net_transit(int at) { + return host[at].transit; +} + +void net_end_transit() { + int at; + + for(at = 0; at < MaxHost; at++) { + host[at].transit = 0; + } +} + +void net_send_batch() { + int at; + int n_unknown = 10; + + for(at = 0;n_unknown && (at < MaxHost); at++) { + if(host[at].addr == 0) { + net_send_query(at + 1); + n_unknown--; + } else { + net_send_ping(at); + } + + if(host[at].addr == remoteaddress.sin_addr.s_addr) { + break; + } + } +} + +int net_preopen() { + char trueopt = 1; + + sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if(sendsock == -1) + return -1; + +#ifdef IP_HDRINCL + /* FreeBSD wants this to avoid sending out packets with protocol type RAW + to the network. */ + if(setsockopt(sendsock, 0, IP_HDRINCL, &trueopt, sizeof(trueopt))) + return -1; +#endif + + recvsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if(recvsock == -1) + return -1; + + return 0; +} + +int net_open(int addr) { + remoteaddress.sin_family = AF_INET; + remoteaddress.sin_addr.s_addr = addr; + + net_send_batch(); + + return 0; +} + +void net_reopen(int addr) { + int at; + + for(at = 0; at < MaxHost; at++) { + memset(&host[at], 0, sizeof(host[at])); + } + + remoteaddress.sin_family = AF_INET; + remoteaddress.sin_addr.s_addr = addr; + + net_send_batch(); +} + +void net_reset() { + int at; + + for(at = 0; at < MaxHost; at++) { + host[at].xmit = host[at].transit; + host[at].returned = 0; + host[at].total = 0; + host[at].best = 0; + host[at].worst = 0; + } + gettimeofday(&reset, NULL); +} + +void net_close() { + close(sendsock); + close(recvsock); +} + +int net_waitfd() { + return recvsock; +} + + diff --git a/net.h b/net.h new file mode 100644 index 0000000..cae195e --- /dev/null +++ b/net.h @@ -0,0 +1,43 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Prototypes for functions in net.c */ + +int net_preopen(); +int net_open(int address); +void net_reopen(int address); +void net_reset(); +void net_close(); +int net_waitfd(); +void net_process_return(); +int net_max(); +int net_addr(int at); +int net_percent(int at); +int net_best(int at); +int net_worst(int at); +int net_avg(int at); +void net_send_batch(); +void net_end_transit(); + +/* Added by Brian Casey, December 1997 bcasey@imagiware.com*/ +int net_returned(int at); +int net_xmit(int at); +int net_transit(int at); + + diff --git a/report.c b/report.c new file mode 100644 index 0000000..9181781 --- /dev/null +++ b/report.c @@ -0,0 +1,71 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "report.h" +#include "net.h" + +void report_open() { + printf("%-40s LOSS RCVD SENT BEST AVG WORST\n", "HOST"); + fflush(stdout); +} + +void report_close() { + int at, max, addr; + int haddr; + char name[81]; + struct hostent *host; + + max = net_max(); + + for(at = 0; at < max; at++) { + addr = net_addr(at); + + strcpy(name, ""); + if(addr == 0) { + sprintf(name, "???"); + } else { + haddr = htonl(addr); + host = gethostbyaddr((char *)&haddr, sizeof(int), AF_INET); + + if(host != NULL) { + strncpy(name, host->h_name, 80); + name[80] = 0; + } else { + sprintf(name, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + } + } + + printf("%-40s%5d%%%6d%5d%6d%6d%6d\n", name, + net_percent(at), + net_returned(at), net_xmit(at), + net_best(at), net_avg(at), net_worst(at)); + } +} + + + diff --git a/report.h b/report.h new file mode 100644 index 0000000..8a8a9b6 --- /dev/null +++ b/report.h @@ -0,0 +1,23 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Prototypes for report.h */ + +void report_open(); +void report_close(); diff --git a/select.c b/select.c new file mode 100644 index 0000000..f68df52 --- /dev/null +++ b/select.c @@ -0,0 +1,147 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "display.h" +#include "dns.h" +#include "net.h" + +extern int Interactive; +extern int MaxPing; +extern float WaitTime; +double dnsinterval; + +void select_loop() { + fd_set readfd; + int anyset; + int action, maxfd; + int dnsfd, netfd; + int NumPing; + struct timeval lasttime, thistime, selecttime, intervaltime; + + NumPing = 0; + anyset = 0; + gettimeofday(&lasttime, NULL); + intervaltime.tv_sec = (int)WaitTime; + intervaltime.tv_usec = 1000000.0 * (WaitTime - floor(WaitTime)); + + while(1) { + FD_ZERO(&readfd); + + maxfd = 0; + + if(Interactive) { + FD_SET(0, &readfd); + maxfd = 1; + } + + dnsfd = dns_waitfd(); + FD_SET(dnsfd, &readfd); + if(dnsfd >= maxfd) + maxfd = dnsfd + 1; + + netfd = net_waitfd(); + FD_SET(netfd, &readfd); + if(netfd >= maxfd) + maxfd = netfd + 1; + + if(anyset) { + selecttime.tv_sec = 0; + selecttime.tv_usec = 0; + + select(maxfd, (void *)&readfd, NULL, NULL, &selecttime); + } else { + if(Interactive) + display_redraw(); + + gettimeofday(&thistime, NULL); + + if(thistime.tv_sec > lasttime.tv_sec + intervaltime.tv_sec || + (thistime.tv_sec == lasttime.tv_sec + intervaltime.tv_sec && + thistime.tv_usec >= lasttime.tv_usec + intervaltime.tv_usec)) { + lasttime = thistime; + if(NumPing >= MaxPing && !Interactive) + break; + NumPing++; + net_send_batch(); + } + + selecttime.tv_usec = (thistime.tv_usec - lasttime.tv_usec); + selecttime.tv_sec = (thistime.tv_sec - lasttime.tv_sec); + if (selecttime.tv_usec < 0) { + --selecttime.tv_sec; + selecttime.tv_usec += 1000000; + } + selecttime.tv_usec = intervaltime.tv_usec - selecttime.tv_usec; + selecttime.tv_sec = intervaltime.tv_sec - selecttime.tv_sec; + if (selecttime.tv_usec < 0) { + --selecttime.tv_sec; + selecttime.tv_usec += 1000000; + } + + if ((selecttime.tv_sec > (time_t)dnsinterval) || + ((selecttime.tv_sec == (time_t)dnsinterval) && + (selecttime.tv_usec > ((time_t)(dnsinterval * 1000000) % 1000000)))) { + selecttime.tv_sec = (time_t)dnsinterval; + selecttime.tv_usec = (time_t)(dnsinterval * 1000000) % 1000000; + } + + select(maxfd, (void *)&readfd, NULL, NULL, &selecttime); + } + + anyset = 0; + + /* Handle any pending resolver events */ + dnsinterval = WaitTime; + dns_events(&dnsinterval); + + /* Has a key been pressed? */ + if(FD_ISSET(0, &readfd)) { + action = display_keyaction(); + + if(action == ActionQuit) + break; + + if(action == ActionReset) + net_reset(); + + anyset = 1; + } + + /* Have we finished a nameservice lookup? */ + if(FD_ISSET(dnsfd, &readfd)) { + dns_ack(); + anyset = 1; + } + + /* Have we got new packets back? */ + if(FD_ISSET(netfd, &readfd)) { + net_process_return(); + anyset = 1; + } + } +} + diff --git a/select.h b/select.h new file mode 100644 index 0000000..33a9a96 --- /dev/null +++ b/select.h @@ -0,0 +1,20 @@ +/* + mtr -- a network diagnostic tool + Copyright (C) 1997,1998 Matt Kimball + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +void select_loop();