diff options
Diffstat (limited to 'lanbrowsing/lisa')
27 files changed, 4540 insertions, 0 deletions
diff --git a/lanbrowsing/lisa/AUTHORS b/lanbrowsing/lisa/AUTHORS new file mode 100644 index 00000000..d13cadc9 --- /dev/null +++ b/lanbrowsing/lisa/AUTHORS @@ -0,0 +1,2 @@ +Written and maintained by + Alexander Neundorf <neundorf@kde.org> diff --git a/lanbrowsing/lisa/COPYING b/lanbrowsing/lisa/COPYING new file mode 100644 index 00000000..ded002ea --- /dev/null +++ b/lanbrowsing/lisa/COPYING @@ -0,0 +1,347 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/lanbrowsing/lisa/ChangeLog b/lanbrowsing/lisa/ChangeLog new file mode 100644 index 00000000..7d0ef7e8 --- /dev/null +++ b/lanbrowsing/lisa/ChangeLog @@ -0,0 +1,8 @@ +0.1.3 +-security fixes: fixed LOGNAME vulnerabilty and another possible buffer overflow + +Version 0.1.2 +-various fixes and improvements... + +Version 0.1 +-initial version diff --git a/lanbrowsing/lisa/INSTALL b/lanbrowsing/lisa/INSTALL new file mode 100644 index 00000000..f8bad0c1 --- /dev/null +++ b/lanbrowsing/lisa/INSTALL @@ -0,0 +1,176 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/kde/bin', `/usr/local/kde/lib', etc. You can specify an +installation prefix other than `/usr/local/kde' by giving `configure' +the option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/lanbrowsing/lisa/Makefile.am b/lanbrowsing/lisa/Makefile.am new file mode 100644 index 00000000..ff1f8433 --- /dev/null +++ b/lanbrowsing/lisa/Makefile.am @@ -0,0 +1,41 @@ +## +AM_CXXFALGS = -fno-rtti -fno-exceptions + +#CXXFLAGS = -fno-rtti -fno-exceptions + +bin_PROGRAMS = lisa reslisa +# +# Programs to compile, Manpages to install and Versions +# +#INCLUDES = $(all_includes) + +lisa_SOURCES = \ +addressvalidator.cpp \ +netmanager.cpp \ +netscanner.cpp \ +ipaddress.cpp \ +main.cpp \ +mystring.cpp \ +client.cpp \ +configfile.cpp + +reslisa_SOURCES = \ +addressvalidator.cpp \ +netmanager.cpp \ +netscanner.cpp \ +ipaddress.cpp \ +strictmain.cpp \ +mystring.cpp \ +client.cpp \ +configfile.cpp + +lisa_LDADD = $(LIBSOCKET) +lisa_LDFLAGS = $(KDE_EXTRA_RPATH) + +reslisa_LDADD = $(LIBSOCKET) +reslisa_LDFLAGS = $(KDE_EXTRA_RPATH) + +misc_DATA = README +miscdir = $(kde_datadir)/lisa +EXTRA_DIST=$(misc_DATA) + diff --git a/lanbrowsing/lisa/NEWS b/lanbrowsing/lisa/NEWS new file mode 100644 index 00000000..898a3dab --- /dev/null +++ b/lanbrowsing/lisa/NEWS @@ -0,0 +1 @@ +See ChangeLog. diff --git a/lanbrowsing/lisa/README b/lanbrowsing/lisa/README new file mode 100644 index 00000000..c6d5d1bd --- /dev/null +++ b/lanbrowsing/lisa/README @@ -0,0 +1,419 @@ +This is the readme to the Lan Information Server LISa and the Restricted LAN +Information Server resLISa. + ++---------------+ +| LISa | ++---------------+ + +LISa is intended to provide a kind of "network neighbourhood" but only +relying on the TCP/IP protocol stack, no smb or whatever. +It is completely independent from KDE/Qt. +The list of running hosts is provided via TCP port 7741. + +LISa supports two ways of finding hosts: + +1. you give LISa a range of of IP-addresses, then LISa will send +ICMP echo requests to all given IP addresses and wait for the answers + +2. you can say LISa to execute "nmblookup "*" ", i.e. the command line tool +nmblookup must be installed, it is part of the samba package. +nmblookup "*" sends a broadcast to the attached networks and all hosts +running smb-services will answer this broadcast + +lisa and reslisa are distributed under the GNU General Public License. + + + +How it works +------------- + +In the configuration file you provide a range of IP-addresses which +LISa should check, wether they are running. In the most simple case +this could be your network address/subnetmask, then LISa would +check every possible host of your network wether it is running. +The hosts are checked using ICMP echo requests. To be able to send and receive +ICMP echo requests and replies the program has to open a so-called +"raw socket". Therefor it needs root privileges. This socket is opened +right after the start of the program, after successfully opening the socket +root privileges are dropped immediately (see main.cpp and strictmain.cpp). +If you configure LISa this way, that it also uses nmblookup, it will +popen("nmblookup \"*\"") and then parse the results. + +Since the ICMP requests and the broadcasts can cause some network traffic +if there are more than one such server running in one network, the servers +cooperate with each other. Before they start pinging (or nmblookup), +they send a broadcast on port 7741. +If somebody answers this broadcast, they will retrieve the complete list +of running hosts via TCP port 7741 from this host and will not start to +ping (or nmblookup) theirselves. If nobody answers, the host which sent +the broadcast will start pinging the hosts (or nmblookup) and then open a +socket which listens for the mentioned broadcasts. If the host received an +answer to his broadcast, it won't have the socket for listening to the +broadcasts open. So usually exactly one of the servers will have this +socket open and only this one will actually ping (or nmblookup) the hosts. +In other words, the servers are lazy, they work like "I will only do something +if nobody else can do it for me". + +There is another feature which reduces the network load. Let's say you configured LISa +to update all 10 minutes. Now you don't access your server very often. +If nobody accesses the server for the last update period, the server will +update (either itself or from the one which actually does the work) and then +double its update period, i.e. the next update will happen after 20 minutes. +This will happen 4 times, so if nobody accesses the server with update period +10 minutes for a long time, its update interval will increase up to +160 minutes, almost three hours. If then somebody accesses the data from the +server, he will get an old list ( up to 160 minutes old). With accessing +the server will reset its update interval to its initial value, +i.e. 10 minutes and immediately start updating if the last update is more +than these 10 minutes over. This means if you get a very old list, you can try +some seconds later again and you should get a current version. +This will have fast effect for the servers, which don't ping (or nmblookup) +theirselves, since only one user usually accesses them, and it will have +less effect for the server which does the pinging (or nmblookup), since +this server is accessed from all other servers in the network. + + +This way it is possible that many hosts in a network run this server, but +the net load will remain low. For the user it is not necessary to know +wether there is a server (i.e. a name server or fileserver or whatever) +in the network which also runs LISa. He can always run LISa locally +and LISa will detect if there is one, transparently to the user. +The first client for LISa is an ioslave for KDE2, so the user +can enter there lan://localhost/ or lan:/, which will both +contact LISa on the own system. +If there is a machine which runs all the time and the user knows +that this machine also runs LISa, he can use his LISa client directly with +this server (would be with the mentioned ioslave lan://the_server_name/). + +If you don't want that your LISa takes part in the broadcasting, but always +does the pinging itself, make it use another port with the +command line option --port or -p. +This is not recommended ! + +If you send SIGHUP to LISa, it will reread its configfile. +If you send SIGUSR1 to LISa, it will print some status information to stdout. + +The data provided over the socket has a simple format: + +<decimal ip address in network byte order><one space 0x20><full name of the host><a terminating '\0'><newline '\n'> +and the last line +0 succeeded<'\n'> + +e.g. + +"17302538 some_host.whatever.de +18285834 linux.whatever.de +17827082 nameserver.whatever.de +0 succeeded +" + +This should make it easy parseable. + +If there are very strict security rules in your network, some people +might consider the pinging a potential attack. If you +have problems with this, try the restricted version, resLISa. + ++------------------+ +| resLISa | ++------------------+ + +If you hav very strict security rules in your network or you don't want to +have another port open or whatever, you can use resLISa. + +With resLISa you can't ping whole networks and address ranges, you can give +resLISa up to currently 64 hosts by their names in its config file. These +will be pinged. You are still able to use nmblookup. +resLISa will also only provide the information over a unix domain socket, i.e. +not over the network. The name of the socket is "/tmp/resLisa-YourLoginname", +so resLISa can be safely run by more users on one machine. +Since it should also not produce a security risk of any kind it is +safe to install reslisa setuid root. root privileges will be dropped +right after startup (see strictmain.cpp), they are only needed to create a raw socket +for sending the ICMP echo requests.. +It will also not send or receive broadcasts. +The first client for this is also an ioslave for KDE2 (makes rlan:/ in e.g. konqy). + + + +Configuration +------------- + +Now an example config file: + +PingAddresses = 192.168.100.0/255.255.255.0;192.168.100.10-192.168.199.19;192.168.200.1;192-192.168-168.100-199.0-9; +PingNames = bb_mail; +AllowedAddresses = 192.168.0.0/255.255.0.0 +BroadcastNetwork = 192.168.100.0/255.255.255.0 +SearchUsingNmblookup = 1 #also try nmblookup +FirstWait = 30 #30 hundredth seconds +SecondWait = -1 #only one try +#SecondWait = 60 #try twice, and the second time wait 0.6 seconds +UpdatePeriod = 300 #update after 300 secs +DeliverUnnamedHosts = 0 #don't publish hosts without name +MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once + + +PingAddresses + +This is probably the most important entry. +Here you say which addresses will be pinged. You can specify multiple +ranges, they are divided by semicolons. + +There are four possible ways to define addresses: +-net address/network mask: 192.168.100.0/255.255.255.0, i.e. an IP address + and the assigned network mask. This doesn't have the real network address + and netmask of your machine, it can be less. E.g. if you have + 10.0.0.0/255.0.0.0, you could specify 10.1.2.0/255.255.255.0 if you are only + interested in these addresses. The combination IP address-network mask + must be divided by a slash "/" and the address does not have to be a real + network address, it can also be a host address of the desired network, + i.e. 10.12.34.67/255.0.0.0 is the same as 10.0.0.0/255.0.0.0 . + +-a range of following IP addresses: 192.168.100.10-192.168.199.19, i.e. + an IP-address where pinging will start and an IP-address where pinging will end. + Both addresses must be divided by a "-". + In this example this would produce 199-100+1=100, 100*256=25.600, + 25.600+(19-10+1)=25.590 addresses + +-an IP-address can be presented by its four decimal numbers, you can specify + ranges four each of these four numbers: 192-192.169-171.100-199.0-9 + In this example all IP addresses with first number 192, second number from + 168 to 168, third number from 100 up to 199 and last number from 0 up + to 9 will be pinged. This would give 1*1*100*10=1.000 addresses. + This is probably only useful in very seldom cases. + Here you have to provide ranges for every four numbers, always divided + by "-". + +-single IP-addresses: 192.168.200.1 + well, single IP addresses or host names + +It is also valid to leave this entry empty. + + +PingNames + here you can additionally specify hosts to ping using their names. + The names have to be divided by semicolons. + +It is also valid to leave this entry empty. + + +AllowedAddresses + + This is also very important. LISa will only ping addresses, + accept clients and answer broadcasts from addresses, which are covered by the + addresses given in this line. You can add up to 32 network addresses/network masks + or single addresses. Divide them by ; and don't put empty space between the + addresses ! + Example: 192.168.0.0/255.255.0.0;192.169.0.0 + -> a complete network and a single address are valid + Always make this as strict as possible, usually + your network address/subnetmask is a good choice. + + +BroadcastNetwork + + This entry contains exactly one network address/subnet mask. + To this network broadcasts will be sent. Usually this should be your + own network address/subnetmask. + Example: 192.168.0.0/255.255.0.0 + + +SearchUsingNmblookup + + Here you can give 0 or 1. + 1 means that LISa will execute "nmblookup "*" " and parse the output + from this command. This produces less network traffic than the pinging, + but you will only get hosts which have a smb-service running (Windows + machines or machines running samba). + If you enable this option and also give IP addresses to ping, then nmblookup + will be executed first and then the pinging will start. + Then only addresses will be pinged, which were not already delivered + from nmblookup. This should slightly decrease the network load. + + +FirstWait + + If LISa pings, i.e. if it sends the ICMP echo requests, it sends a bunch + of requests at once, and the it will wait for the number of hundredth seconds + you specify here. Usually values from 5 to 50 should be good, the maximum + is 99 (gives 0.99 seconds, a very long time). + Try to make this value as small as possible while still finding all + running hosts. + + +SecondWait + + After LISa sent the echo requests the first time, it can be possible + that some hosts were not found. To improve the results, LISa can ping a + second time. This time it will only ping hosts, from which it didn't receive + answers. If you have good results with pinging only once, you can disable + the second time with setting SecondWait to -1. + Otherwise it might be a good idea to make this value a little bit bigger + than the value for FirstWait, since the hosts which were not found + on the first try, are probably slower or further away so they might take + some milliseconds longer to answer. + Usually values from 5 to 50 should be good or -1 to disable the second scan. + The maximum is 99 (gives 0.99 seconds, a very long time). + + +UpdatePeriod + + This is the interval after which LISa will update, i.e. ping or nmblookup + or get the list of hosts from the LISa server which actually does the pinging. + Valid values are between 30 seconds and 1800 seconds (half an hour). + If you have a big network, don't make the interval to small (to keep + network load low). Values from 300 to 900 seconds (5 to 15 minutes) might be + a good idea. Keep in mind that the update period is doubled + if nobody accesses the server, up to 4 times, so the interval will become + 16 times the value given here and will be reseted to the value given here + if somebody accesses the server. + + +DeliverUnnamedHosts + + If an answer to an echo request from an IP address was received, were LISa + could not determine a name, it will be only delivered over the port + if you set this to 1. + I am not really sure if this is a useful feature, but maybe + there are some infrastructure devices in your network without assigned names, + so they don't have to be published. Set this to 0 if you want to keep them + secret ;-) + If unsure, say 0. + +MaxPingsAtOnce + + When sending the pings (echo requests), LISa sends a bunch of these at once + and then waits for the answers. By default there are 256 pings sent at once, + usually you should not need the change this value. If you make it much bigger, + the internal receive buffers for the answers to the echo requests may become to small, + if you make it to small, the updating will be slower. + + +Three different example config files: + +You are member of a small network with 24 bit network mask, i.e. +up to 256 hosts: + +PingAddresses = 192.168.100.0/255.255.255.0 +AllowedAddresses = 192.168.100.0/255.255.255.0 +BroadcastNetwork = 192.168.100.0/255.255.255.0 +SearchUsingNmblookup = 0 #don't use nmblookup +FirstWait = 20 #20 hundredth seconds +SecondWait = 30 #30 hundredth seconds on the seconds try +UpdatePeriod = 300 #update after 300 secs +DeliverUnnamedHosts = 0 #don't publish hosts without name + + +You are only interested in hosts running smb services and you don't have +routers in your network: + +AllowedAddresses = 192.168.100.0/255.255.255.0 +BroadcastNetwork = 192.168.100.0/255.255.255.0 +SearchUsingNmblookup = 1 #use nmblookup +UpdatePeriod = 300 #update after 300 secs +DeliverUnnamedHosts = 0 #don't publish hosts without name + + +The same network, but here both nmblookup and pinging is used. + +PingAddresses = 192.168.100.0/255.255.255.0 +PingNames = bb_mail +AllowedAddresses = 192.168.0.0/255.255.0.0 +BroadcastNetwork = 192.168.100.0/255.255.255.0 +SearchUsingNmblookup = 1 #also try nmblookup +FirstWait = 30 #30 hundredth seconds +SecondWait = -1 #only one try +#SecondWait = 60 #try twice, and the second time wait 0.6 seconds +UpdatePeriod = 300 #update after 300 secs +DeliverUnnamedHosts = 0 #don't publish hosts without name +MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once + +And now a configuration file for resLISa, PingAddresses is not used by resLISa, +neither is BroadcastNetwork. + +PingNames = bb_mail;some_host;some_other_host +AllowedAddresses = 192.168.0.0/255.255.0.0 +SearchUsingNmblookup = 1 # use nmblookup +FirstWait = 30 #30 hundredth seconds +SecondWait = -1 #only one try +#SecondWait = 60 #try twice, and the second time wait 0.6 seconds +UpdatePeriod = 300 #update after 300 secs +DeliverUnnamedHosts = 1 #also publish hosts without name +MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once + + ++----------------------+ +| Installation | ++----------------------+ + +Both reslisa and lisa open a so called raw socket to send and receive +ICMP echo requests (pings). To do this, they need root privileges. + +lisa offers a service on TCP port 7741, it should be installed by root +and started when the system comes up, it depends on your distribution +how to do this. + +reslisa is intended to be started per user, it doesn't offer anything to +the network. It needs to be installed setuid root. + +If you use the rlan-ioslave from KDE2, reslisa can be started automatically +by reslisa. + +lisa reads the file lisarc, reslisa reads the file reslisarc. +If you want to be able to configure both from the KDE Control Center, +you have to start them using the command line switch -K. + +For more information where they look for configuration files read +the next chapter. + + ++--------------------------------------------+ +| Command Line Options and other usage | ++--------------------------------------------+ + +The following command line options are supported: + +-v, --version prints a short version info +-h, --help gives an overview over teh command line options + +Some options regarding search order for the configuration files. +For reslisa the file is named reslisarc instead lisarc. + +-u, --unix search at first for $HOME/.lisarc, then + for /etc/lisarc, this is the default behaviour + +-k, --kde1 search at first for $HOME/.kde/share/config/lisarc, + then for $KDEDIR/share/config/lisarc + +-K, --kde2 looks for the file lisarc in every directory + returned by running "kde-config --path config" + +-c, --config=FILE read this and no other configuration file + + +This one is only available for LISa, not for resLISa. + +-p, --port PORTNR start the server om this portnumber + if you use this LISa won't be able to + cooperate with other LISa's in the network + + +If you send the Hangup-Signal to lisa or reslisa, it will reread its +configuration file (killall -HUP lisa). + +If you send the User1-Signal to lisa or reslisa, it will print some status +information to the standard output (killall -USR1 lisa). You won't see +anything if the console from which lisa/reslisa was started has terminated. + + +LISa and resLISa need a libstdc++ (it uses only the string-class from it), +it *doesn't* need neither Qt nor KDE. + +So, that's it for now. +If you have suggestions, problems or whatever, contact me. + + +Have fun +Alexander Neundorf +<neundorf@kde.org> + diff --git a/lanbrowsing/lisa/addressvalidator.cpp b/lanbrowsing/lisa/addressvalidator.cpp new file mode 100644 index 00000000..aa292806 --- /dev/null +++ b/lanbrowsing/lisa/addressvalidator.cpp @@ -0,0 +1,228 @@ +/* addressvalidator.cpp + * + * Copyright (c) 2000, Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "addressvalidator.h" +#include "mystring.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <iostream> +using namespace std; + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif +#define LISA_DEBUG 0 +#define dcerr if (LISA_DEBUG==1) std::cerr<<"AddressValidator::" + +AddressValidator::AddressValidator(const MyString& addressSpecs) +//this is 127.0.0.0 +:localhostNet(htonl(0x7f000000)) +//with mask 255.255.255.0 +,localhostMask(htonl(0xffffff00)) +{ + clearSpecs(); + MyString tmp=addressSpecs; + setValidAddresses(tmp); +} + +AddressValidator::AddressValidator() + //this is 127.0.0.0 + :localhostNet(htonl(0x7f000000)) + //with mask 255.255.255.0 + ,localhostMask(htonl(0xffffff00)) +{ + clearSpecs(); +} + +AddressValidator::~AddressValidator() +{} + +void AddressValidator::configure(Config& config) +{ + MyString tmp=stripWhiteSpace(config.getEntry("AllowedAddresses","")); + tmp=tmp+";"; + setValidAddresses(tmp); + dcerr<<"configure(): "<<tmp<<std::endl; +} + + +void AddressValidator::setValidAddresses(MyString addressSpecs) +{ + dcerr<<"setValidAddresses"<<std::endl; + allowedHosts=addressSpecs; + MyString nextPart; + while (!addressSpecs.isEmpty()) + { + dcerr<<"setValidAddresses: "<<addressSpecs<<std::endl; + int pos=addressSpecs.find(";"); + nextPart=addressSpecs.left(pos); + addressSpecs=addressSpecs.mid(pos+1); + dcerr<<"setValidAddresses: nextPart: "<<nextPart<<std::endl; + if ((nextPart.contains('.')==3) && (nextPart.contains('-')==0)) + { + addSpec(EXACTADDR_SPEC,inet_addr(nextPart.data())); + dcerr<<"setValidAddresses: exact addr: " + <<std::ios::hex<<inet_addr(nextPart.data())<<std::ios::dec<<std::endl; + } + else if ((nextPart.contains('-')==1) && (nextPart.contains('.')==6)) + { + int p2=nextPart.find("-"); + MyString from=nextPart.left(p2); + MyString to=nextPart.mid(p2+1); + addSpec(RANGE_SPEC,ntohl(inet_addr(from.data())),ntohl(inet_addr(to.data()))); + } + else if ((nextPart.contains('-')==4) && (nextPart.contains('.')==3)) + { + unsigned int i1=0; + unsigned int i2=0; + int p2=0; + MyString rest=nextPart+'.'; + MyString digit; + + for (int i=0; i<4; i++) + { + p2=rest.find("-"); + digit=rest.left(p2); + i1=i1<<8; + i1+=atoi(digit.data()); + rest=rest.mid(p2+1); + p2=rest.find("."); + digit=rest.left(p2); + i2=i2<<8; + i2+=atoi(digit.data()); + rest=rest.mid(p2+1); + }; + addSpec(MULTIRANGE_SPEC,i1,i2); + } + else + { + pos=nextPart.find('/'); + MyString netStr=nextPart.left(pos); + MyString maskStr=nextPart.mid(pos+1); + int mask=inet_addr(maskStr.data()); + int net= (inet_addr(netStr.data()) & mask); + dcerr<<"setValidAddresses: net/mask: " + <<std::ios::hex<<net<<"/"<<mask<<std::ios::dec<<std::endl; + addSpec(NETMASK_SPEC,net,mask); + } + } +} + +void AddressValidator::clearSpecs() +{ + allowedHosts=""; + for (int i=0; i<MAX_SPECS; i++) + { + specs[i].address=0; + specs[i].mask=0; + specs[i].typeOfSpec=NO_SPEC; + } +} + +void AddressValidator::addSpec(int type, int address, int mask) +{ + for (int i=0; i<MAX_SPECS; i++) + { + if (specs[i].typeOfSpec==NO_SPEC) + { + specs[i].address=address; + specs[i].mask=mask; + specs[i].typeOfSpec=type; + return; + } + } +} + +int AddressValidator::isValid(int addressNBO) +{ + dcerr<<"isValid: " + <<std::ios::hex<<addressNBO<<std::ios::dec<<std::endl; + //localhost is always allowed + dcerr<<"isValid() local net: "<< + std::ios::hex<<localhostNet<<" mask: "<<localhostMask<<" AND: "<<(addressNBO & + localhostMask)<<std::ios::dec<<std::endl; + if ((addressNBO & localhostMask) == localhostNet) + return 1; + + for (int i=0; i<MAX_SPECS; i++) + { + if (specs[i].typeOfSpec==NO_SPEC) + { + //since the specifications are always entered from the beginning + //of the array, we already passed the last one if we get here + //so we can return now "it is invalid !" ;-) + return 0; + //continue; + } + else if (specs[i].typeOfSpec==EXACTADDR_SPEC) + { + dcerr<<"isValid: comparing " + <<std::ios::hex<<specs[i].address<<std::ios::dec<<std::endl; + if (addressNBO==specs[i].address) + { + dcerr<<"isValid: exact address"<<std::endl; + return 1; // this one is allowed to :-) + } + } + else if (specs[i].typeOfSpec==NETMASK_SPEC) + { + dcerr<<"isValid: ANDing "<< + std::ios::hex<<(addressNBO & specs[i].mask)<<" "<< + specs[i].address<<std::ios::dec<<std::endl; + if ((addressNBO & specs[i].mask) == specs[i].address) + { + dcerr<<"isValid: net/mask"<<std::endl; + return 1; + } + } + else if (specs[i].typeOfSpec==RANGE_SPEC) + { + if ((ntohl(addressNBO)>=specs[i].address) && (ntohl(addressNBO)<=specs[i].mask)) + { + dcerr<<"isValid: range"<<std::endl; + return 1; + } + } + else if (specs[i].typeOfSpec==MULTIRANGE_SPEC) + { + unsigned int addr=ntohl(addressNBO); + dcerr<<"isValid ntohl="<<hex<<addr<<" addr: "<<specs[i].address<<" ma: "<<specs[i].mask<<dec<<std::endl; + unsigned int mask=0x000000ff; + int failure=0; + for (int j=0; j<4; j++) + { + dcerr<<"isValid "<<hex<<"mask="<<mask<<" addr="<<(addr&mask)<<" addr="<<(specs[i].address & mask)<<" ma="<<(specs[i].mask&mask)<<std::endl; + if (((addr & mask) < (specs[i].address & mask)) + || ((addr & mask) > (specs[i].mask & mask))) + { + failure=1; + break; + } + mask=mask<<8; + } + dcerr<<"isValid: multirange"<<std::endl; + if (!failure) + return 1; + } + } + //if ((addressNBO==htonl(0x0a040801)) || (addressNBO==htonl(0xc0a80001))) return 0; + dcerr<<"isValid: invalid address"<<std::endl; + return 0; +} + diff --git a/lanbrowsing/lisa/addressvalidator.h b/lanbrowsing/lisa/addressvalidator.h new file mode 100644 index 00000000..6f1c723e --- /dev/null +++ b/lanbrowsing/lisa/addressvalidator.h @@ -0,0 +1,55 @@ +/* addressvalidator.h + * + * Copyright (c) 2000, Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef ADDRESSVALIDATOR_H +#define ADDRESSVALIDATOR_H + +#include "lisadefines.h" +#include "mystring.h" +#include "configfile.h" + +#define NO_SPEC 0 +#define NETMASK_SPEC 1 +#define EXACTADDR_SPEC 2 +#define RANGE_SPEC 3 +#define MULTIRANGE_SPEC 4 + +struct AddressSpec +{ + int address; + int mask; + int typeOfSpec; +}; + +class AddressValidator +{ + public: + AddressValidator(const MyString& addressSpecs); + AddressValidator(); + ~AddressValidator(); + void configure(Config& config); + void setValidAddresses(MyString addressSpecs); + void clearSpecs(); + int isValid(int addressNBO); + MyString validAddresses() {return allowedHosts;}; + private: + int localhostNet; + int localhostMask; + MyString allowedHosts; + void addSpec(int type, int address, int mask=0); + AddressSpec specs[MAX_SPECS]; +}; + +#endif diff --git a/lanbrowsing/lisa/client.cpp b/lanbrowsing/lisa/client.cpp new file mode 100644 index 00000000..21069a17 --- /dev/null +++ b/lanbrowsing/lisa/client.cpp @@ -0,0 +1,122 @@ +/* client.cpp + * + * Copyright (c) 2000, Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "netmanager.h" +#include "client.h" +#include "lisadefines.h" + +#include <iostream> +#include <unistd.h> + +#include <strings.h> + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif + +#ifdef dcerr +#undef dcerr +#endif + +#define LISA_DEBUG 0 +#define dcerr if (LISA_DEBUG==1) std::cerr<<"Client::" + + +Client::Client(NetManager* parent, int socketFD, int closeOnDelete) +:parentServer(parent) +,m_fd(socketFD) +,m_done(0) +,m_closeOnDelete(closeOnDelete) +{ + dcerr<<"ctor created new client"<<std::endl; + if (m_fd==-1) m_done=1; + m_creationTime=time(0); +} + +Client::Client() +:parentServer(0) +,m_fd(-1) +,m_done(1) +,m_closeOnDelete(1) +{ + m_creationTime=time(0); +} + +Client::Client(const Client& c) +:parentServer(c.parentServer) +,m_fd(c.m_fd) +,m_creationTime(c.m_creationTime) +,m_done(c.m_done) +,m_closeOnDelete(1) +{ + dcerr<<"Client copy ctor"<<std::endl; +} + +int Client::tryToGetInfo() +{ + dcerr<<"tryToGetInfo: m_done: "<<m_done<<std::endl; + if (done()) return 1; + if (m_fd==-1) + { + close(); + return 1; + } + dcerr<<"tryToGetInfo: calling data.getFile()"<<std::endl; + if (!parentServer->isInformed()) return 0; + //we fork now, so that writing to the client can't block the server process + /* int pid=fork(); + if (pid==-1) + { + //parent + dcerr<<"NetScanner::scan: error occurred"<<std::endl; + return 1; + } + else if (pid!=0) + { + //parent + return 1; + };*/ + //child + //this one does it all :-) + dcerr<<"tryToGetInfo: sending data to client"<<std::endl; + parentServer->writeDataToFD(fd(),0); + close(); + //exit(0); + return 1; +} + +void Client::close() +{ + if (m_fd!=-1) ::close(m_fd); + m_fd=-1; + m_done=1; +} + +void Client::read() +{ + dcerr<<"read()"<<std::endl; + if (m_fd==-1) return; + char buf[1024]; + //the clients should not send us something ! + int result=::read(m_fd,buf,1024); + if (result>=0) close(); +} + +Client::~Client() +{ + if (m_closeOnDelete) + close(); +} + diff --git a/lanbrowsing/lisa/client.h b/lanbrowsing/lisa/client.h new file mode 100644 index 00000000..efcf5f86 --- /dev/null +++ b/lanbrowsing/lisa/client.h @@ -0,0 +1,45 @@ +/* client.h + * + * Copyright (c) 2000, Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <time.h> + +class NetManager; + +class Client +{ + public: + Client(NetManager* parent, int socketFD, int closeOnDelete=1); + Client(); + Client(const Client& c); + ~Client(); + int tryToGetInfo(); + int done() {return m_done;}; + int fd() {return m_fd;}; + void read(); + void close(); + time_t age() {return time(0)-m_creationTime;}; + private: + NetManager *parentServer; + int m_fd; + //if something goes wrong close the connection after a timeout + time_t m_creationTime; + int m_done; + int m_closeOnDelete; +}; + +#endif diff --git a/lanbrowsing/lisa/configfile.cpp b/lanbrowsing/lisa/configfile.cpp new file mode 100644 index 00000000..5c1e4a65 --- /dev/null +++ b/lanbrowsing/lisa/configfile.cpp @@ -0,0 +1,136 @@ +/* configfile.cpp + * + * Copyright (c) 1998, 2000, Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "configfile.h" + +#include <iostream> +#include <fstream> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif +#define LISA_DEBUG 0 + +#ifdef dcerr +#undef dcerr +#endif + +#define dcerr if (LISA_DEBUG==1) std::cerr<<"Config::" + + +#define CFGBUFSIZE 16*1024 + +Config::Config(const MyString& name/*,String path*/) +{ + char buff[CFGBUFSIZE],c; +/* String s,empty="#############################################################################################################################################################"; + String home=getenv("HOME"); + + if (!home.empty()) home+=String("/")+name; + if (fexists(home)==0) + { + home=path+"/"+name; + if (fexists(home)==0) + { + home=name; + if (fexists(home)==0) return; + }; + };*/ + std::ifstream inf(name.data()); + if (!inf) + { + std::cout<<"could not open file "<<name<<std::endl; + return; + } + dcerr<<"Config(): opened file "<<name<<std::endl; + //read the file + char key[CFGBUFSIZE], value[CFGBUFSIZE]; + do + { + char* buffStart=buff; + //inf.getline(buff,16*1024,'\n'); + int bufSize(CFGBUFSIZE); + int lineBroken(0); + do + { + lineBroken=0; + inf.get(buffStart,bufSize,'\n'); + inf.get(c); + int l=strlen(buffStart); + if (l==0) + break; + if (buffStart[l-1]=='\\') + { + buffStart=buffStart+l-1; + bufSize=bufSize+1-l; + lineBroken=1; + } + } while ((lineBroken) && (!inf.eof())); + //make it ignore comments + char *theChar=strchr(buff,'#'); + if (theChar!=0) + *theChar='\0'; + //now divide the line into key and value + theChar=strchr(buff,'='); + if (theChar!=0) + { + *theChar='\0'; + key[0]='\0'; + sscanf(buff,"%8000s",key); + //do we have something valid ? + if (key[0]!='\0') + { + //the char behind the = should be at least the terminating \0 + // so I can be sure to access valid memory here, IMO + value[0]='\0'; + + strncpy(value,theChar+1,CFGBUFSIZE); + if (value[0]!='\0') + { + //here we can be sure that the list will only contain + //strings which are at least one char long + dcerr<<"Config(): adding "<<key<<std::endl; + m_entries[key]=value; + } + } + } + } + while (!inf.eof()); +} + +MyString Config::getEntry(const char *key, const char* defaultValue) +{ + if ((key==0) || (key[0]=='\0')) + return defaultValue; + if (m_entries.find(key)==m_entries.end()) + return defaultValue; + return m_entries[key]; +} + +int Config::getEntry(const char *key, int defaultValue) +{ + char def[100]; + sprintf(def,"%d",defaultValue); + MyString tmp=stripWhiteSpace(getEntry(key,def)); + int i(0); + int ok=sscanf(tmp.c_str(),"%d",&i); + if (ok==1) return i; + return defaultValue; +} + diff --git a/lanbrowsing/lisa/configfile.h b/lanbrowsing/lisa/configfile.h new file mode 100644 index 00000000..1879e3b9 --- /dev/null +++ b/lanbrowsing/lisa/configfile.h @@ -0,0 +1,33 @@ +/* configfile.h + * + * Copyright (c) 1998,2000 Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + +#include <map> + +#include "mystring.h" + +class Config +{ + public: + Config(const MyString& name); + MyString getEntry(const char *key, const char* defaultValue); + int getEntry(const char *key, int defaultValue); + protected: + std::map<MyString, MyString> m_entries; +}; + +#endif diff --git a/lanbrowsing/lisa/ipaddress.cpp b/lanbrowsing/lisa/ipaddress.cpp new file mode 100644 index 00000000..8dc98194 --- /dev/null +++ b/lanbrowsing/lisa/ipaddress.cpp @@ -0,0 +1,97 @@ +/* ipaddress.cpp + * + * Copyright (c) 1998, 1999, Alexander Neundorf, Lukas Pustina + * alexander.neundorf@rz.tu-ilmenau.de + * lukas@tronet.de + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "ipaddress.h" + +#include <iostream> + +IPAddress::IPAddress() +{ +} + +IPAddress::IPAddress(const MyString& ip) +{ + if (ip.length()==0) + s="0.0.0.0"; + else s=ip; + i=string2Int(s); + n=string2Struct(s); +} + +IPAddress::IPAddress(unsigned int ip) +{ + i=ip; + s=int2String(i); + n.s_addr=htonl(i); + //n=string2Struct(s); +} + +IPAddress::IPAddress(struct in_addr ip) +{ + n=ip; + s=struct2String(n); + i=string2Int(s); +} + +IPAddress::IPAddress(int b1, int b2, int b3, int b4) +{ + char tmp[30]; + sprintf(tmp,"%i.%i.%i.%i",b1,b2,b3,b4); + s=tmp; + i=string2Int(s); + n=string2Struct(s); +} + + +MyString IPAddress::int2String(unsigned int ip) +{ + MyString tmp(""); + struct in_addr addr; + addr.s_addr=htonl(ip); + tmp=inet_ntoa(addr); + return tmp; +} + +MyString IPAddress::struct2String(struct in_addr ip) +{ + return MyString(inet_ntoa(ip)); +} + +unsigned int IPAddress::string2Int(MyString ip) +{ +// struct in_addr addr; +// inet_aton(ip.c_str(),&addr); +// cout<<addr.s_addr<<endl; +// return ntohl(addr.s_addr); + return ntohl(inet_addr(ip.c_str())); +} + +struct in_addr IPAddress::string2Struct(MyString ip) +{ + struct in_addr tmp; +// inet_aton(ip.c_str(),&tmp); + tmp.s_addr = inet_addr(ip.c_str()); + return tmp; +} + +void IPAddress::print() +{ + std::cout<<"address as string: "<<s<<std::endl; + std::cout<<"address in host byte order: "<<std::ios::hex<<i<<std::ios::dec<<std::endl; + std::cout<<"address in network byte order: "<<std::ios::hex<<n.s_addr<<std::ios::dec<<std::endl; +} + + diff --git a/lanbrowsing/lisa/ipaddress.h b/lanbrowsing/lisa/ipaddress.h new file mode 100644 index 00000000..ecca928a --- /dev/null +++ b/lanbrowsing/lisa/ipaddress.h @@ -0,0 +1,55 @@ +/* ipaddress.h + * + * Copyright (c) 1998, 1999, Alexander Neundorf, Lukas Pustina + * alexander.neundorf@rz.tu-ilmenau.de + * lukas@tronet.de + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef IPADDRESS_H +#define IPADDRESS_H + +#include "lisadefines.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include "mystring.h" + +class IPAddress +{ + public: + IPAddress(); + IPAddress(const MyString& ip); + IPAddress(unsigned int ip); + IPAddress(struct in_addr ip); + IPAddress(int b1, int b2, int b3, int b4); + //you know + MyString asString() {return s;}; + //in host byte order + unsigned int asInt() {return i;}; + //in network byte order + struct in_addr asStruct() {return n;}; + /*operator= (IPAddress ip); + operator+= (unsigned int add);*/ + void print(); + private: + MyString int2String(unsigned int ip); + MyString struct2String(struct in_addr ip); + unsigned int string2Int(MyString ip); + struct in_addr string2Struct(MyString ip); + MyString s; + unsigned int i; + struct in_addr n; +}; + +#endif diff --git a/lanbrowsing/lisa/lisadefines.h b/lanbrowsing/lisa/lisadefines.h new file mode 100644 index 00000000..ac15d2c5 --- /dev/null +++ b/lanbrowsing/lisa/lisadefines.h @@ -0,0 +1,19 @@ +#ifndef LISADEFINES_H +#define LISADEFINES_H + + +#define MYVERSION "0.2.3" +#define MY_ID 7741 +#define MYPORT 7741 +#define MAX_SPECS 128 +#define SELECT_TIMEOUT 7 + +#define EXTRACONFIGSTYLE 0 +#define UNIXCONFIGSTYLE 1 + +#define CONFIGFILEBASENAME "lisarc" +#define STRICTCONFIGFILEBASENAME "reslisarc" + +#define STRICTMODEMAXHOSTS 64 + +#endif diff --git a/lanbrowsing/lisa/main.cpp b/lanbrowsing/lisa/main.cpp new file mode 100644 index 00000000..7de59389 --- /dev/null +++ b/lanbrowsing/lisa/main.cpp @@ -0,0 +1,290 @@ +/* main.cpp + * + * Copyright (c) 1998-2000 Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "lisadefines.h" +#include "netmanager.h" + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif + +#ifdef dcerr +#undef dcerr +#endif + +#define LISA_DEBUG 0 +#define dcerr if (LISA_DEBUG==1) std::cerr<<"main " + +#include <iostream> +#include <signal.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> + +// detect linux/glibc for the gnu style --args +#if defined(__linux__) || defined(__linux) || defined(linux) +# include <features.h> +# ifdef __GLIBC__ +// only gnu libc has getopt.h... getopt(3) is defined to be in unistd.h +// by POSIX.2 +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +# include <getopt.h> +# endif // __GLIBC__ +# define GNU_GETOPT +#endif // linux + + +void printVersion() +{ + const char * versionInfo=\ + "\r\nThis is the LAN Information Server LISa "MYVERSION"\r\n"\ + "It is free software according the GNU General Public License\r\n"\ + "Copyright (c) 2000-2003 by Alexander Neundorf\r\n"\ + "email: neundorf@kde.org\r\n"; + std::cout<<versionInfo<<std::endl; +} + +void usage() +{ + printVersion(); + const char * usageInfo=\ + "-v, --version prints out a short version info\n"\ + "-u, --unix deprecated\n"\ + "-k, --kde1 deprecated\n"\ + "-K, --kde2 deprecated\n"\ + " lisa now looks always first for $(HOME)/.lisarc, then for /etc/lisarc\n"\ + "-c, --config=FILE read this and no other configuration file\n"\ + "-q, --quiet start quiet without the greeting message\n"\ + "-p, --port PORTNR start the server on this portnumber\n"\ + " if you use this LISa won't be able to\n"\ + " cooperate with other LISa's in the network\n"\ + "-h, --help you are currently reading it ;-)\n"; + std::cout<<usageInfo<<std::endl; +//I thought this would be the way to check wether long options are supported... +//#ifndef _GNU_SOURCE +// std::cout<<"Please note that the long options are not supported on +// this system"<<std::endl; +//#endif +} + +NetManager *manager(0); + + +void destruct(int sigNumber) +{ + signal(sigNumber,SIG_IGN); + dcerr<<"signal caught: "<<sigNumber<<", exiting"<<std::endl; + //signal(sigNumber,&destruct); + if (manager!=0) + manager->~NetManager(); + exit(0); +} + +void readConfig(int sigNumber) +{ + dcerr<<"readConfig(): signal caught: "<<sigNumber<<std::endl; + signal(SIGHUP,SIG_IGN); + if (manager!=0) + manager->readConfig(); + signal(SIGHUP,&readConfig); +} + +void printState(int sigNumber) +{ + dcerr<<"printState(): signal caught: "<<sigNumber<<std::endl; + signal(SIGUSR1,SIG_IGN); + if (manager!=0) + manager->printState(); + signal(SIGUSR1,&printState); +} + +void setSignalHandler() +{ + signal(SIGHUP,&readConfig); + signal(SIGUSR1,&printState); + + signal(SIGINT,&destruct); + signal(SIGQUIT,&destruct); + signal(SIGILL,&destruct); + signal(SIGTRAP,&destruct); + signal(SIGABRT,&destruct); + signal(SIGBUS,&destruct); + signal(SIGSEGV,&destruct); + signal(SIGUSR2,&destruct); + signal(SIGPIPE,&destruct); + signal(SIGALRM,&destruct); + signal(SIGTERM,&destruct); + signal(SIGFPE,&destruct); +#ifdef SIGPOLL + signal(SIGPOLL, &destruct); +#endif +#ifdef SIGSYS + signal(SIGSYS, &destruct); +#endif +#ifdef SIGVTALRM + signal(SIGVTALRM, &destruct); +#endif +#ifdef SIGXCPU + signal(SIGXCPU, &destruct); +#endif +#ifdef SIGXFSZ + signal(SIGXFSZ, &destruct); +#endif +} + +#ifdef GNU_GETOPT +static struct option const long_opts[] = +{ + {"version", no_argument, 0, 'v'}, + {"quiet", no_argument, 0, 'q'}, + {"unix", no_argument, 0, 'u'}, + {"kde1", no_argument, 0, 'k'}, + {"kde2", no_argument, 0, 'K'}, + {"config", required_argument, 0, 'c'}, + {"port", required_argument, 0, 'p'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} +}; +#endif + +int main(int argc, char** argv) +{ + int quiet(0); + int c(0); + int configStyle(UNIXCONFIGSTYLE); + MyString configFile; + int portToUse(MYPORT); + +//I thought this would be the way to check wether long options are supported... +#ifdef GNU_GETOPT + while ((c=getopt_long(argc, argv, "vqukKc:h", long_opts, 0))!=-1) +#else + while ((c=getopt(argc, argv, "vqukKc:h"))!=-1) +#endif + + { + char *endp(0); + switch (c) + { + case 0: + break; + case 'v': + printVersion(); + exit(0); + break; + case 'q': + quiet=1; + break; + case 'u': + case 'k': + case 'K': + std::cout<<"\a\nThe command line switches -k, -K, -u and \ntheir long versions "\ + "--kde1, --kde2 and --unix are not supported anymore.\n"\ + "Lisa will always first look for $(HOME)/.lisarc , then for /etc/lisarc.\n"\ + "If your lisa configuration file was created using an older version of \n"\ + "the KDE control center, copy the /root/.kde/share/config/lisarc to /etc and \n"\ + "then start lisa without any command line options.\n"<<std::endl; + return 0; + break; + + case 'c': + configFile = optarg; + configStyle = EXTRACONFIGSTYLE; + break; + + case 'p': + portToUse=strtol(optarg,&endp,10); + if (endp!=0) + { + usage(); + exit(0); + } + break; + + case 'h': + default: + usage(); + exit(0); + break; + } + } + + //fork and let the parent exit + pid_t pid=fork(); + if (pid>0) + { + //this is the parent + exit(0); + } + else if (pid<0) + { + std::cout<<"could not fork()"<<std::endl; + exit(0); + } + //we will only read/write to/from this socket in the child process + int rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); + if (rawSocket==-1) + { + std::cout<<"could not create raw socket, root privileges required"<<std::endl; + std::cout<<"take a look at the README for more information"<<std::endl; + exit(0); + } + int bufferSize(60*1024); + int on(1); + setsockopt(rawSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, + sizeof(bufferSize)); + int result=setsockopt(rawSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on, + sizeof(on)); + dcerr<<"setsockopt returns "<<result<<std::endl; + //dropping root privileges + //they will be regained once in the child process + //for creating a raw socket + + //now dropping root privileges once and ever + + + setuid(getuid()); + if (geteuid() != getuid()) + _exit(255); + + //according to R. Stevens the following three lines + //make daemons feel good :) + setsid(); + chdir("/"); + umask(0); + + dcerr<<"starting, dropped root privileges"<<std::endl; + dcerr<<"port: "<<portToUse<<" file: "<<configFile<<std::endl; + NetManager netmanager(rawSocket,portToUse,configFile,configStyle,0); + manager=&netmanager; + dcerr<<"NetManager created"<<std::endl; + setSignalHandler(); + + netmanager.readConfig(); + if (netmanager.prepare()) + { + if (!quiet) + { + printVersion(); + std::cout<<"\n\rrunning on port "<<portToUse<<"\n\rHave fun ! :-)"<<std::endl; + } + netmanager.run(); + }; + dcerr<<"server finished"<<std::endl; +} diff --git a/lanbrowsing/lisa/mystring.cpp b/lanbrowsing/lisa/mystring.cpp new file mode 100644 index 00000000..2b7af2d2 --- /dev/null +++ b/lanbrowsing/lisa/mystring.cpp @@ -0,0 +1,52 @@ +#include "mystring.h" + +#include <ctype.h> + +//this one is taken from Qt/QCString + +MyString stripWhiteSpace(MyString str) +{ + if ( str.isEmpty() ) // nothing to do + return ""; + + char const *s = str.data(); + MyString result = s; + int reslen = result.length(); + if ( !isspace(s[0]) && !isspace(s[reslen-1]) ) + return result; // returns a copy + + s = result.data(); + int start = 0; + int end = reslen - 1; + while ( isspace(s[start]) ) // skip white space from start + start++; + if ( s[start] == '\0' ) + { // only white space + result.resize( 1 ); + return ""; + } + + while ( end && isspace(s[end]) ) // skip white space from end + end--; + + end -= start - 1; + + result=str.mid(start,end); + //memmove( result.data(), &s[start], end ); + //result.resize( end + 1 ); + return result; +} + +//mainly taken from qcstring +int MyString::contains(char c) +{ + int count = 0; + char const *d = c_str(); + if ( d==0 ) + return 0; + while ( *d ) + if ( *d++ == c ) + count++; + return count; +} + diff --git a/lanbrowsing/lisa/mystring.h b/lanbrowsing/lisa/mystring.h new file mode 100644 index 00000000..20d40e59 --- /dev/null +++ b/lanbrowsing/lisa/mystring.h @@ -0,0 +1,42 @@ +#ifndef MYSTRING_H +#define MYSTRING_H + +#include <string> +#include <string.h> +#include <strings.h> +#include <stdio.h> + +class MyString: public std::string +{ + public: + MyString(const char c) : std::string(1,c) {}; + MyString(const char* c) + : std::string(c==0?"":c) + {}; + MyString(const std::string& s) : std::string(s) {}; + MyString() : std::string() {}; + + int isEmpty() const {return empty();}; + MyString mid(int pos, int length=-1) const {return substr(pos,length);}; + MyString left(int len) const {return substr(0,len);}; + MyString right(int len) const + { + if (len<1) return ""; + else if (len<int(size())) return substr(size()-len); + else return (*this); + }; + + int contains(char c); + //char & operator[] (unsigned int i) {return ((string)(*this))[i];} + //operator const char* () const {return c_str();} + const char* data() const {return c_str();} + /*void setNum(int value) {char c[15]; bzero(c,15); sprintf(c,"%d",value); (*this)=c;}; + void setNum(double value) {char c[25]; bzero(c,25); sprintf(c,"%g",value); (*this)=c;}; + void simplifyWhiteSpace();*/ +}; + +//taken from Qt/QCString +MyString stripWhiteSpace(MyString str); + +#endif + diff --git a/lanbrowsing/lisa/netmanager.cpp b/lanbrowsing/lisa/netmanager.cpp new file mode 100644 index 00000000..e652ce97 --- /dev/null +++ b/lanbrowsing/lisa/netmanager.cpp @@ -0,0 +1,1058 @@ + /* netmanager.cpp + * + * Copyright (c) 2000, Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "config.h" + +#include "netmanager.h" +#include "lisadefines.h" + +#include <iostream> +#include <unistd.h> + +#include <sys/un.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <strings.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <pwd.h> + +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif + +#ifdef dcerr +#undef dcerr +#endif + +#ifdef mdcerr +#undef mdcerr +#endif + +#define LISA_DEBUG 0 +#define dcerr if (LISA_DEBUG==1) std::cerr<<"NetManager " +#define mdcerr if (LISA_DEBUG==1) std::cerr<<procId<<" NetManager " + +static void sig_child_handler(int) +{ + int saved_errno = errno; + + while (waitpid(-1, NULL, WNOHANG) > 0) + ; // repeat + + errno = saved_errno; +} + +NetManager::NetManager(int& rawSocketFD, int portToUse, MyString configFile, int configStyle, int strictMode) +:NetScanner(rawSocketFD,strictMode) +//,validator() +,m_listenFD(-1) +,m_bcFD(-1) +,m_basePort(portToUse) +,m_pipeFD(-1) +,m_receiveBuffer(0) +,m_receivedBytes(0) +,m_childPid(0) +,m_lastUpdate(0) + +,m_isInformed(0) +,m_isBeingScanned(0) +,m_firstRun(1) +,m_serverServer(0) +,m_servedThisPeriod(0) + +,m_serveCount(0) +,m_refreshTime(60) +,m_initialRefreshTime(60) +,m_increasedRefreshTime(0) +,m_broadcastAddress(0) +,m_extraConfigFile(configFile) +,m_configStyle(configStyle) +,m_usedConfigFileName("") +{ + mdcerr<<"NetManager::NetManager"<<std::endl; + m_startedAt=time(0); + + struct sigaction act; + act.sa_handler=sig_child_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGCHLD); + // Make sure we don't block this signal. gdb tends to do that :-( + sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0); + + act.sa_flags = SA_NOCLDSTOP; +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif + + sigaction( SIGCHLD, &act, NULL ); +} + +NetManager::~NetManager() +{ + mdcerr<<"NetManager destructor ..."<<std::endl; + if (m_receiveBuffer!=0) delete [] m_receiveBuffer; + ::close(m_listenFD); + ::close(m_bcFD); +} + +void NetManager::readConfig() +{ + m_usedConfigFileName=getConfigFileName(); + if (m_usedConfigFileName.isEmpty()) + { + std::cout<<"configfile not found"<<std::endl; + std::cout<<"use the command line option --help and \ntake a look at the README for more information"<<std::endl; + exit(1); + } + + Config config(m_usedConfigFileName); + NetManager::configure(config); + NetScanner::configure(config); + validator.configure(config); + //after reading the new configuration we should really update + m_lastUpdate=0; +} + +void NetManager::configure(Config& config) +{ + m_refreshTime=config.getEntry("UpdatePeriod",300); + MyString tmp=stripWhiteSpace(config.getEntry("BroadcastNetwork","0.0.0.0/255.255.255.255;")); + tmp=tmp+";"; + mdcerr<<"NetManager::readConfig: "<<tmp<<std::endl; + MyString netAddressStr=tmp.left(tmp.find('/')); + tmp=tmp.mid(tmp.find('/')+1); + tmp=tmp.left(tmp.find(';')); + mdcerr<<"NetManager::readConfig: broadcastNet "<<netAddressStr<<" with mask "<<tmp<<std::endl; + int netMask=inet_addr(tmp.c_str()); + int netAddress=inet_addr(netAddressStr.c_str()); + m_broadcastAddress= netAddress | (~netMask); + mdcerr<<"NetManager::readConfig: net "<<std::ios::hex<<netAddress<<" with mask "<<netMask<<" gives "<<m_broadcastAddress<<std::endl; + + //maybe this way we can avoid that all servers on the net send + //their requests synchronously, since now the refreshtime isn't + //always the eact value of m_refreshTime, but differs always slightly + if ((m_refreshTime%SELECT_TIMEOUT)==0) m_refreshTime+=2; + //some limits from half a minute to half an hour + if (m_refreshTime<30) m_refreshTime=30; + if (m_refreshTime>1800) m_refreshTime=1800; + m_initialRefreshTime=m_refreshTime; +} + +int NetManager::prepare() +{ + mdcerr<<"NetManager::prepare"<<std::endl; + + ::close(m_listenFD); + ::close(m_bcFD); + int result(0); + if (m_strictMode) + { + m_listenFD=::socket(AF_LOCAL, SOCK_STREAM, 0); + //m_listenFD=::socket(AF_LOCAL, SOCK_STREAM, IPPROTO_TCP); + MyString socketName("/tmp/resLisa-"); + struct passwd *user = getpwuid( getuid() ); + if ( user ) + socketName+=user->pw_name; + else + //should never happen + socketName+="???"; + ::unlink(socketName.data()); + sockaddr_un serverAddr; + if (socketName.length() >= sizeof(serverAddr.sun_path)) + { + std::cout<<"NetManager::prepare: your user name \""<<user->pw_name<<"\" is too long, exiting."<<std::endl; + return 0; + } + memset((void*)&serverAddr, 0, sizeof(serverAddr)); + serverAddr.sun_family=AF_LOCAL; + strncpy(serverAddr.sun_path,socketName.data(),sizeof(serverAddr.sun_path)); + result=::bind(m_listenFD,(sockaddr*) &serverAddr,sizeof(serverAddr)); + if (result!=0) + { + std::cout<<"NetManager::prepare: bind (UNIX socket) failed, errno: "<<errno<<std::endl; + return 0; + } + } + else + { + //create a listening port and listen + m_listenFD = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_listenFD==-1) + { + std::cout<<"NetManager::prepare: socket(TCP) failed, errno: "<<errno<<std::endl; + return 0; + } + + sockaddr_in serverAddress; +// bzero((char*)&serverAddress, sizeof(serverAddress)); + memset((void*)&serverAddress, 0, sizeof(serverAddress)); + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(m_basePort); + + int on(1); + result=setsockopt(m_listenFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + if (result!=0) + { + std::cout<<"NetManager::prepare: setsockopt(SO_REUSEADDR) failed"<<std::endl; + return 0; + } + result=::bind(m_listenFD, (struct sockaddr *) &serverAddress, sizeof(serverAddress)); + if (result!=0) + { + std::cout<<"NetManager::prepare: bind (TCP) failed, errno: "<<errno<<std::endl; + return 0; + } + } + result=::listen(m_listenFD, 32); + if (result!=0) + { + std::cout<<"NetManager::prepare: listen failed"<<std::endl; + return 0; + } + mdcerr<<"NetManager::prepare: listening on port "<<m_basePort<<"..."<<std::endl; + + return 1; +} + +void NetManager::generateFDset(fd_set *tmpFDs) +{ + mdcerr<<"NetManager::generateFDset"<<std::endl; + + FD_ZERO(tmpFDs); + FD_SET(m_listenFD,tmpFDs); + mdcerr<<"NetManager::generateFDset: adding listen FD="<<m_listenFD<<std::endl; +// for (Client* tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + if (tmpClient->fd()!=-1) + { + mdcerr<<"NetManager::generateFDset: adding client FD="<<tmpClient->fd()<<std::endl; + FD_SET(tmpClient->fd(),tmpFDs); + } + + if (m_pipeFD!=-1) + { + mdcerr<<"NetManager::generateFDset: adding pipeFD="<<m_pipeFD<<std::endl; + FD_SET(m_pipeFD,tmpFDs); + } + + if ((m_bcFD!=-1) && (!m_strictMode)) + { + mdcerr<<"NetManager::generateFDset: adding m_bcFD="<<m_bcFD<<std::endl; + FD_SET(m_bcFD,tmpFDs); + } +} + +int NetManager::waitForSomethingToHappen(fd_set *tmpFDs) +{ + mdcerr<<"NetManager::waitForSomethingToHappen for 10 seconds"<<std::endl; + if (m_firstRun) + { + tv.tv_sec = 1; + m_firstRun=0; + } + else + tv.tv_sec = SELECT_TIMEOUT; + + tv.tv_usec = 0; + //generateFDset(tmpFDs); + int result=select(getMaxFD(),tmpFDs,0,0,&tv); + if (result>0) return 1; + else return 0; +} + +int NetManager::getMaxFD() +{ + int maxFD(m_listenFD); +// for (Client* tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + if (tmpClient->fd()>maxFD) + maxFD=tmpClient->fd(); + + if (m_pipeFD>maxFD) maxFD=m_pipeFD; + if (m_bcFD>maxFD) maxFD=m_bcFD; + + mdcerr<<"NetManager::getMaxFD()="<<maxFD+1<<std::endl; + return maxFD+1; +} + +int fileReadable(const MyString& filename) +{ + FILE *file=::fopen(filename.data(), "r"); + if (file==0) + return 0; + fclose(file); + return 1; +} + +MyString NetManager::getConfigFileName() +{ + MyString tmpBase(CONFIGFILEBASENAME); + + if (m_strictMode) + tmpBase=STRICTCONFIGFILEBASENAME; + + if (!m_extraConfigFile.isEmpty()) + m_configStyle=EXTRACONFIGSTYLE; + + MyString tmpFilename; + if (m_configStyle==EXTRACONFIGSTYLE) + { + tmpFilename=m_extraConfigFile; + if (fileReadable(tmpFilename)) + return tmpFilename; + return ""; + } + else if (m_configStyle==UNIXCONFIGSTYLE) + { + tmpFilename=getenv("HOME"); + tmpFilename+=MyString("/.")+tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + tmpFilename="/etc/"; + tmpFilename+=tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + return ""; + } +/* else if (m_configStyle==KDE1CONFIGSTYLE) + { + tmpFilename=getenv("HOME"); + tmpFilename+=MyString("/.kde/share/config/")+tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + tmpFilename=getenv("KDEDIR"); + tmpFilename+=MyString("/share/config/")+tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + return ""; + } + else if (m_configStyle==KDE2CONFIGSTYLE) + { + FILE *kdeConfig=popen("kde-config --path config","r"); + if (kdeConfig==0) + { + std::cout<<"could not execute kde-config, check your KDE 2 installation\n"<<std::endl; + return ""; + }; + //this should be large enough + char buf[4*1024]; + memset(buf,0,4*1024); + fgets(buf,4*1024-1,kdeConfig); + // Warning: The return value of plcose may be incorrect due to the + // SIGCHLD handler that is installed. Ignore it! + pclose(kdeConfig); + int length=strlen(buf); + if (buf[length-1]=='\n') buf[length-1]='\0'; + MyString kdeDirs(buf); + while (kdeDirs.contains(':')) + { + MyString nextDir=kdeDirs.left(kdeDirs.find(':')); + kdeDirs=kdeDirs.mid(kdeDirs.find(':')+1); + nextDir=nextDir+tmpBase; + mdcerr<<"trying to open "<<nextDir<<std::endl; + if (fileReadable(nextDir)) + return nextDir; + }; + kdeDirs=kdeDirs+tmpBase; + mdcerr<<"trying to open "<<kdeDirs<<std::endl; + if (fileReadable(kdeDirs)) + return kdeDirs; + kdeDirs="/etc/"; + kdeDirs=kdeDirs+tmpBase; + if (fileReadable(kdeDirs)) + return kdeDirs; + return ""; + }*/ + return ""; +} + +int NetManager::run() +{ + int continueMainLoop(1); + //not forever + while(continueMainLoop) + { + mdcerr<<"\nNetManager::run: next loop: "<<clients.size()<<" clients"<<std::endl; + fd_set tmpFDs; + generateFDset(&tmpFDs); + + int result=waitForSomethingToHappen(&tmpFDs); + mdcerr<<"NetManager::run: something happened..."<<std::endl; + //timeout + if (result==0) + { + mdcerr<<"NetManager::run: serverServer=="<<m_serverServer<<std::endl; + mdcerr<<"NetManager::run: scanning after timeout"<<std::endl; + scan(); + } + else + { + //a new connection ? + if (FD_ISSET(m_listenFD,&tmpFDs)) + { + mdcerr<<"NetManager::run: on m_listenFD"<<std::endl; + struct sockaddr_in clientAddress; + socklen_t clilen(sizeof(clientAddress)); +// bzero((char*)&clientAddress, clilen); + memset((void*)&clientAddress,0,sizeof(clientAddress)); + int connectionFD=::accept(m_listenFD,(struct sockaddr *) &clientAddress, &clilen); + if ((validator.isValid(clientAddress.sin_addr.s_addr)) || (m_strictMode)) + { + mdcerr<<"NetManager::run: adding client"<<std::endl; + addClient(connectionFD); + m_servedThisPeriod=1; + m_refreshTime=m_initialRefreshTime; + m_increasedRefreshTime=0; + } + else + { + mdcerr<<"NetManager::run: kicking client"<<std::endl; + ::close(connectionFD); + } + } + checkClientsAndPipes(&tmpFDs); + } + serveAndClean(); + } + return 1; +} + +void NetManager::addClient(int socketFD) +{ + mdcerr<<"NetManager::addClient on FD="<<socketFD<<std::endl; + if (socketFD==-1) return; + Client c(this,socketFD,0); + clients.push_back(c); +} + +void NetManager::checkClientsAndPipes(fd_set *tmpFDs) +{ + mdcerr<<"NetManager::checkClientsAndPipes()"<<std::endl; + //actually the clients should not send anything +// for (Client *tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + { + mdcerr<<"NetManager::checkClientsAndPipes: checking client"<<std::endl; + if (FD_ISSET(tmpClient->fd(),tmpFDs)) + { + mdcerr<<"NetManager::checkClientsAndPipes: client sent something"<<std::endl; + tmpClient->read(); + } + } + + //now check wether we received a broadcast + //m_bcFD should always be -1 in strictMode + if ((m_bcFD!=-1) && (!m_strictMode)) + { + mdcerr<<"NetManager::checkClientsAndPipe: checking bcFD"<<std::endl; + //yes ! + if (FD_ISSET(m_bcFD,tmpFDs)) + answerBroadcast(); + } + + //read the stuff from the forked pipe + if (m_pipeFD!=-1) + { + mdcerr<<"NetManager::checkClientsAndPipe: checking pipe"<<std::endl; + if (FD_ISSET(m_pipeFD,tmpFDs)) + { + mdcerr<<"NetManager::checkClientsAndPipes: pipe sent something"<<std::endl; + int result=readDataFromFD(m_pipeFD); + if (result!=1) + { + ::close(m_pipeFD); + m_pipeFD=-1; + mdcerr<<"NetManager::checkClientsAndPipes: everything read from pipe from proc "<<m_childPid<<std::endl; + processScanResults(); + } + } + } +} + +void NetManager::answerBroadcast() +{ + //actually we should never get here in strictMode + if (m_strictMode) return; + + //this one is called only in checkClientsAndPipes() + //if we are sure that we received something on m_bcFD + //so we don't need to check here again + + mdcerr<<"NetManager::answerBroadcast: received BC"<<std::endl; + struct sockaddr_in sAddr; + socklen_t length(sizeof(sockaddr_in)); + char buf[1024]; + int result=recvfrom(m_bcFD, (char*)buf, 1024, 0, (sockaddr*)&sAddr,&length); + mdcerr<<"NetManager::answerBroadcast: received successfully"<<std::endl; + //did recvfrom() succeed ? + //our frame is exactly MYFRAMELENGTH bytes big, if the received one has a different size, + //it must be something different + if (result!=MYFRAMELENGTH) return; + //if it has the correct size, it also must have the correct identifier + MyFrameType *frame=(MyFrameType*)(void*)buf; + if ((ntohl(frame->id)!=MY_ID) || + ((ntohl(frame->unused1)==getpid()) && (ntohl(frame->unused2)==m_startedAt))) + { + mdcerr<<"NetManager::answerBroadcast: must be the same machine"<<std::endl; + return; + } + + //mdcerr<<"received "<<ntohl(buf[0])<<" from "<<inet_ntoa(sAddr.sin_addr)<<hex<<" "; + //mdcerr<<sAddr.sin_addr.s_addr<<" //"<<ntohl(sAddr.sin_addr.s_addr)<<dec<<std::endl; + //will we answer this request ? + if (!validator.isValid(sAddr.sin_addr.s_addr)) + { + mdcerr<<"NetManager::answerBroadcast: invalid sender"<<std::endl; + return; + } + + //create the answering socket + + int answerFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (answerFD==-1) + { + mdcerr<<"NetManager::answerBroadcast: could not create answering socket"<<std::endl; + return; + } + + sAddr.sin_family=AF_INET; + sAddr.sin_port=htons(m_basePort+1); + MyFrameType answerFrame; + answerFrame.id=htonl(MY_ID); + answerFrame.unused1=0; + answerFrame.unused2=0; + //don't care about the result + mdcerr<<"NetManager::answerBroadcast: sending answer"<<std::endl; + + result=::sendto(answerFD,(char*)&answerFrame,sizeof(answerFrame),0,(sockaddr*)&sAddr,length); + mdcerr<<"sent "<<result<<" byte using sendto"<<std::endl; + ::close(answerFD); + //sent answer +} + +void NetManager::serveAndClean() +{ + //try to serve the request +// for (Client *tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + { + mdcerr<<"NetManager::serveAndClean: trying to get info"<<std::endl; + tmpClient->tryToGetInfo(); + } + + //try to delete served the clients + /** PM: substituted STL code + for (Client* tmpClient=clients.first();tmpClient!=0; tmpClient=clients.next()) + { + //if we served the client or if he's already half a minute + //connected remove it + //this way we get rid of clients if something went wrong and + //maybe it's even a security point, I don't know + if ((tmpClient->done()) || (tmpClient->age()>30)) + { + mdcerr<<"NetManager::serveAndClean: removing Client"<<std::endl; + clients.remove(tmpClient); + tmpClient=clients.first(); + } + }*/ + clients.remove_if(client_is_done()); +} + +void NetManager::scan() +{ + mdcerr<<"NetManager::scan()"<<std::endl; + if (isBeingScanned()) return; + + time_t currentTime=time(0); + mdcerr<<"currentTime: "<<currentTime<<" lastUpdate: "<<m_lastUpdate<<std::endl; + if ((currentTime-m_lastUpdate)<m_refreshTime) return; + mdcerr<<"NetManager::scan: scanning..."<<std::endl; + + m_isBeingScanned=1; + int fileDescr[2]; + ::pipe(fileDescr); + mdcerr<<"NetScanner::scan: file-descr[0]: "<<fileDescr[0]<<std::endl; + mdcerr<<"NetScanner::scan: file-descr[1]: "<<fileDescr[1]<<std::endl; + int pid=fork(); + if (pid==-1) + { + mdcerr<<"NetScanner::scan: error occurred"<<std::endl; + return; + } + else if (pid!=0) + { + //parent + ::close(fileDescr[1]); + m_pipeFD=fileDescr[0]; + m_childPid=pid; + return; + } + //child + procId="** child ** "; + mdcerr<<" NetManager::scan: a child was born"<<std::endl; + if (m_strictMode) + { + mdcerr<<" NetManager::scan: scanning myself in strict mode, becoming serverServer"<<std::endl; + doScan(); + //in the child we don't have to call setServerServer() + //since this opens the listening socket, what has to be done + //in the parent process + m_serverServer=1; + } + else + { + int serverAddress=findServerServer(); + if (serverAddress==0) + { + mdcerr<<" NetManager::scan: scanning myself, becoming serverServer"<<std::endl; + doScan(); + //in the child we don't have to call setServerServer() + //since this opens the listening socket, what has to be done + //in the parent process + m_serverServer=1; + } + else + { + mdcerr<<" NetManager::scan: getting list from serverServer"<<std::endl; + getListFromServerServer(serverAddress); + m_serverServer=0; + } + } + mdcerr<<" NetScanner::scan: sending information to parent process"<<std::endl; + writeDataToFD(fileDescr[1],m_serverServer); + mdcerr<<" NetScanner::scan: closed FD: "<<::close(fileDescr[1])<<std::endl; + mdcerr<<" NetScanner::scan: exiting now"<<std::endl; + ::exit(0); +} + +int NetManager::writeDataToFD(int fd, int serverServer) +{ + mdcerr<<" NetManager::writeDataToFD fd="<<fd<<std::endl; + m_serveCount++; + char buffer[1024]; + + int length; +// for (Node* tmpNode=hostList->first(); tmpNode!=0; tmpNode=hostList->next()) + for (std::list<Node>::iterator tmpNode=hostList->begin(); tmpNode!=hostList->end(); tmpNode++) + { + sprintf(buffer,"%u %s\n",tmpNode->ip,tmpNode->name.left(1000).c_str()); + length=strlen(buffer)+1; + const char *currentBuf=buffer; + //make sure that everything is written + while (length>0) + { + int result=::write(fd,currentBuf,length); + mdcerr<<"NetManager::writeDataToFD: wrote "<<result<<" bytes"<<std::endl; + if (result==-1) + { + perror("hmmpf: "); + return 0; + } + length-=result; + currentBuf+=result; + } + } + //and a last line + sprintf(buffer,"%d succeeded\n",serverServer); + length=strlen(buffer)+1; + const char *currentBuf=buffer; + //make sure that everything is written + while (length>0) + { + int result=::write(fd,currentBuf,length); + if (result==-1) return 0; + length-=result; + currentBuf+=result; + } + return 1; +} + +int NetManager::readDataFromFD(int fd) +{ + mdcerr<<"NetManager::readDataFromFD"<<std::endl; + char tmpBuf[64*1024]; + int result=::read(fd,tmpBuf,64*1024); + mdcerr<<"NetManager::readDataFromFD: read "<<result<<" bytes"<<std::endl; + if (result==-1) return -1; + if (result==0) + { + mdcerr<<"NetManager::readDataFromFD: FD was closed"<<std::endl; + return 0; + } + char *newBuf=new char[m_receivedBytes+result]; + if (m_receiveBuffer!=0) memcpy(newBuf,m_receiveBuffer,m_receivedBytes); + memcpy(newBuf+m_receivedBytes,tmpBuf,result); + m_receivedBytes+=result; + if (m_receiveBuffer!=0) delete [] m_receiveBuffer; + m_receiveBuffer=newBuf; + // too much data - abort at 2MB to avoid memory exhaustion + if (m_receivedBytes>2*1024*1024) + return 0; + + return 1; +} + +int NetManager::processScanResults() +{ + mdcerr<<"NetManager::processScanResults"<<std::endl; + if (m_receiveBuffer==0) return 0; + std::list<Node> *newNodes=new std::list<Node>; + + char *tmpBuf=m_receiveBuffer; + int bytesLeft=m_receivedBytes; + mdcerr<<"m_receivedBytes: "<<m_receivedBytes<<" bytesLeft: "<<bytesLeft<<std::endl; + //this should be large enough for a name + //and the stuff which is inserted into the buffer + //comes only from ourselves ... or attackers :-( + char tmpName[1024*4]; + while (bytesLeft>0) + { + int tmpIP=2; // well, some impossible IP address, 0 and 1 are already used for the last line of output + tmpName[0]='\0'; + if ((memchr(tmpBuf,0,bytesLeft)==0) || (memchr(tmpBuf,int('\n'),bytesLeft)==0)) + { + delete newNodes; + hostList->clear(); + + m_lastUpdate=time(0); + delete [] m_receiveBuffer; + m_receiveBuffer=0; + m_receivedBytes=0; + m_isInformed=1; + m_isBeingScanned=0; + return 0; + } + //mdcerr<<"NetManager::processScanResults: processing -"<<tmpBuf; + //since we check for 0 and \n with memchr() we can be sure + //at this point that tmpBuf is correctly terminated + int length=strlen(tmpBuf)+1; + if (length<(4*1024)) + sscanf(tmpBuf,"%u %s\n",&tmpIP,tmpName); + + bytesLeft-=length; + tmpBuf+=length; + mdcerr<<"length: "<<length<<" bytesLeft: "<<bytesLeft<<std::endl; + if ((bytesLeft==0) && ((tmpIP==0) ||(tmpIP==1)) && (strstr(tmpName,"succeeded")!=0)) + { + mdcerr<<"NetManager::processScanResults: succeeded :-)"<<std::endl; + delete hostList; + hostList=newNodes; + + m_lastUpdate=time(0); + delete [] m_receiveBuffer; + m_receiveBuffer=0; + m_receivedBytes=0; + m_isInformed=1; + m_isBeingScanned=0; + adjustRefreshTime(tmpIP); + enableServerServer(tmpIP); + //m_serverServer=tmpIP; + + return 1; + } + else if (tmpIP!=2) + { + //mdcerr<<"NetManager::processScanResults: adding host: "<<tmpName<<" with ip: "<<tmpIP<<std::endl; + newNodes->push_back(Node(tmpName,tmpIP)); + } + } + //something failed :-( + delete newNodes; + hostList->clear(); + + m_lastUpdate=time(0); + delete [] m_receiveBuffer; + m_receiveBuffer=0; + m_receivedBytes=0; + m_isInformed=1; + m_isBeingScanned=0; + + mdcerr<<"NetScanner::processScanResult: finished"<<std::endl; + return 0; +} + +void NetManager::adjustRefreshTime(int serverServer) +{ + //we are becoming server server + if (((m_serverServer==0) && (serverServer)) || (m_servedThisPeriod)) + { + m_increasedRefreshTime=0; + m_refreshTime=m_initialRefreshTime; + } + //nobody accessed the server since the last update + //so increase the refresh time + //this should happen more seldom to the serverServer + //than to others + else + { + //up to the 16 times refresh time + if (m_increasedRefreshTime<4) + { + m_increasedRefreshTime++; + m_refreshTime*=2; + }; + }; + m_servedThisPeriod=0; + +} + +void NetManager::enableServerServer(int on) +{ + mdcerr<<"NetManager::enableServerServer: "<<on<<std::endl; + //in strictMode we don't listen to broadcasts from the network + if (m_strictMode) return; + + m_serverServer=on; + if (on) + { + //nothing has to be done + if (m_bcFD!=-1) return; + // otherwise create the socket which will listen for broadcasts + sockaddr_in my_addr; + my_addr.sin_family=AF_INET; + my_addr.sin_port=htons(m_basePort); + my_addr.sin_addr.s_addr=0; + + m_bcFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (m_bcFD==-1) + { + mdcerr<<"NetManager::enableServerServer: socket() failed"<<std::endl; + m_serverServer=0; + return; + }; + int on(1); + int result=setsockopt(m_bcFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + if (result!=0) + { + mdcerr<<"NetManager::enableServerServer: setsockopt(SO_REUSEADDR) failed"<<std::endl; + m_serverServer=0; + ::close(m_bcFD); + m_bcFD=-1; + return; + }; + result=::bind(m_bcFD, (struct sockaddr *) &my_addr, sizeof(my_addr)); + if (result!=0) + { + mdcerr<<"NetManager::enableServerServer: bind (UDP) failed"<<std::endl; + m_serverServer=0; + ::close(m_bcFD); + m_bcFD=-1; + return; + }; + } + else + { + ::close(m_bcFD); + m_bcFD=-1; + }; +} + +int NetManager::findServerServer() +{ + mdcerr<<" NetManager::findServerServer"<<std::endl; + //actually this should never be called in strictMode + if (m_strictMode) return 0; + if (!validator.isValid(m_broadcastAddress)) + { + mdcerr<<" NetManager::findServerServer: invalid broadcastAddress"<<std::endl; + return 0; + }; + //create a socket for sending the broadcast + //we don't have to set SO_REUSEADDR, since we don't call bind() + //to this socket + int requestFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (requestFD==-1) + { + mdcerr<<" NetManager::findServerServer: could not create request socket"<<std::endl; + return 0; + }; + + int on(1); + //this is actually the only socket which will send broacasts + int result=setsockopt(requestFD, SOL_SOCKET, SO_BROADCAST,(char*)&on, sizeof(on)); + if (result!=0) + { + mdcerr<<"setsockopt(SO_BROADCAST) failed"<<std::endl; + ::close(requestFD); + return 0; + }; + + //create a socket for receiving the answers + int answerFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (answerFD==-1) + { + mdcerr<<" NetManager::findServerServer"<<std::endl; + ::close(requestFD); + return 0; + }; + + //since this socket will be bound, we have to set SO_REUSEADDR + result=setsockopt(answerFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + if (result!=0) + { + mdcerr<<"setsockopt(SO_REUSEADDR) failed"<<std::endl; + ::close(answerFD); + ::close(requestFD); + return 0; + }; + + sockaddr_in my_addr; + my_addr.sin_family=AF_INET; + my_addr.sin_port=htons(m_basePort+1); + my_addr.sin_addr.s_addr=0; + //this one has to be bound + result=::bind(answerFD, (struct sockaddr *) &my_addr, sizeof(my_addr)); + if (result!=0) + { + mdcerr<<"bind (UDP) failed"<<std::endl; + ::close(answerFD); + ::close(requestFD); + return 0; + }; + + //now send the broadcast + struct sockaddr_in sAddr; + sAddr.sin_addr.s_addr=m_broadcastAddress; + sAddr.sin_family=AF_INET; + sAddr.sin_port=htons(m_basePort); + socklen_t length(sizeof(sockaddr_in)); + mdcerr<<" NetManager::findServerServer: broadcasting to: " + <<std::ios::hex<<m_broadcastAddress<<std::ios::dec<<std::endl; + + MyFrameType requestFrame; + requestFrame.id=htonl(MY_ID); + requestFrame.unused1=htonl(getppid()); + requestFrame.unused2=htonl(m_startedAt); + + result=::sendto(requestFD,(char*)&requestFrame,sizeof(requestFrame),0,(sockaddr*)&sAddr,length); + ::close(requestFD); + if (result!=MYFRAMELENGTH) + { + mdcerr<<" NetManager::findServerServer: sent wrong number of bytes: "<<result<<std::endl; + ::close(answerFD); + return 0; + }; + //wait for an answer + struct timeval tv; + tv.tv_sec=0; + tv.tv_usec=1000*250; //0.1 sec + fd_set fdSet; + FD_ZERO(&fdSet); + FD_SET(answerFD,&fdSet); + result=select(answerFD+1,&fdSet,0,0,&tv); + if (result<0) + { + mdcerr<<" NetManager::findServerServer: select() failed: "<<result<<std::endl; + mdcerr<<" NetManager::findServerServer: answerFD="<<answerFD<<std::endl; + perror("select:"); + ::close(answerFD); + return 0; + } + if (result==0) + { + mdcerr<<" NetManager::findServerServer: nobody answered "<<std::endl; + ::close(answerFD); + return 0; + } + mdcerr<<"received offer "<<std::endl; + struct sockaddr_in addr; + length=sizeof(sockaddr_in); + char buf[1024]; + result=recvfrom(answerFD, (char*)buf, 1024, 0, (sockaddr*) &addr,&length); + if (result!=MYFRAMELENGTH) + { + mdcerr<<" NetManager::findServerServer: wrong number of bytes: "<<result<<std::endl; + ::close(answerFD); + return 0; + }; + MyFrameType *frame=(MyFrameType*)(void*)buf; + //wrong identifier ? + if (ntohl(frame->id)!=MY_ID) + { + mdcerr<<" NetManager::findServerServer: wrong id"<<std::endl; + ::close(answerFD); + return 0; + }; + + mdcerr<<"received from "<<inet_ntoa(addr.sin_addr)<<std::endl; + + ::close(answerFD); + //return the ip of the server server in network byte order + return addr.sin_addr.s_addr; +} + +void NetManager::getListFromServerServer( int address) +{ + mdcerr<<"NetManager::getListFromServerServer"<<std::endl; + //actually we should never get here in strictMode + if (m_strictMode) return; + //open a tcp socket to the serverserver + int serverServerFD=::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverServerFD==-1) + return; + sockaddr_in addr; + //we get the address already in network byte order + addr.sin_addr.s_addr=address; + addr.sin_family=AF_INET; + addr.sin_port=htons(m_basePort); + int result=::connect(serverServerFD,(sockaddr*)&addr,sizeof(addr)); + if (result!=0) + { + ::close(serverServerFD); + return; + }; + do + { + result=readDataFromFD(serverServerFD); + } while (result==1); + ::close(serverServerFD); + processScanResults(); + mdcerr<<"NetManager::getListFromServerServer succeeded"<<std::endl; +} + +void NetManager::printState() +{ + std::cerr<<"LAN Information Server Lisa "MYVERSION"\nAlexander Neundorf <neundorf@kde.org>\n"; + std::cerr<<"Reading options from config file: "<<m_usedConfigFileName<<std::endl; + std::cerr<<"StrictMode: "<<m_strictMode<<std::endl; + std::cerr<<"ServerServer: "<<m_serverServer<<std::endl; + std::cerr<<"UseNmblookup: "<<m_useNmblookup<<std::endl; + std::cerr<<"Pinging: "<<ipRangeStr<<std::endl; + std::cerr<<"Allowed hosts: "<<validator.validAddresses()<<std::endl; + std::cerr<<"Broadcasting to: "<<std::ios::hex<<ntohl(m_broadcastAddress)<<std::ios::dec<<std::endl; + std::cerr<<"Initial update period: "<<m_initialRefreshTime<<" seconds"<<std::endl; + std::cerr<<"Current update period: "<<m_refreshTime<<" seconds"<<std::endl; + std::cerr<<"Last update: "<<time(0)-m_lastUpdate<<" seconds over"<<std::endl; + std::cerr<<"Waiting "<<m_firstWait<<" 1/100th seconds for echo answers on the first try"<<std::endl; + std::cerr<<"Waiting "<<m_secondWait<<" 1/100th seconds for echo answers on the second try"<<std::endl; + std::cerr<<"Sending "<<m_maxPings<<" echo requests at once"<<std::endl; + std::cerr<<"Publishing unnamed hosts: "<<m_deliverUnnamedHosts<<std::endl; + std::cerr<<"Already served "<<m_serveCount<<" times"<<std::endl; +} + +//this one is not used at the moment +/*int NetManager::uptime() +{ + return (time(0)-m_startedAt); +};*/ diff --git a/lanbrowsing/lisa/netmanager.h b/lanbrowsing/lisa/netmanager.h new file mode 100644 index 00000000..dee5fb27 --- /dev/null +++ b/lanbrowsing/lisa/netmanager.h @@ -0,0 +1,109 @@ +/* netmanager.h + * + * Copyright (c) 1998, 1999, Alexander Neundorf + * alexander.neundorf@rz.tu-ilmenau.de + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef NETMANAGER_H +#define NETMANAGER_H + +#include <list> + +#include "netscanner.h" +#include "client.h" +#include "mystring.h" +#include "configfile.h" +#include "lisadefines.h" + +struct MyFrameType +{ + int id; + //keep some room for later extensions + int unused1; + int unused2; + int unused3; +}; + +#define MYFRAMELENGTH 16 + +class NetManager:public NetScanner +{ + public: + NetManager(int& rawSocketFD, int portToUse, MyString configFile, int configStyle=UNIXCONFIGSTYLE, int strictMode=0); + ~NetManager(); + + int prepare(); + int run(); + int writeDataToFD(int fd, int serverServer); + + void readConfig(); + void configure(Config& config); + int isBeingScanned() {return m_isBeingScanned;}; + int isInformed() {return m_isInformed;}; + //int uptime(); + void printState(); + protected: + int m_listenFD; + int m_bcFD; + int m_basePort; + + int m_pipeFD; + char *m_receiveBuffer; + size_t m_receivedBytes; + + struct timeval tv; + pid_t m_childPid; + time_t m_lastUpdate; + time_t m_startedAt; + + int m_isInformed; + int m_isBeingScanned; + int m_firstRun; + int m_serverServer; + int m_servedThisPeriod; + + int m_serveCount; + int m_refreshTime; + int m_initialRefreshTime; + int m_increasedRefreshTime; + int m_broadcastAddress; + std::list<Client> clients; + struct client_is_done : std::unary_function<Client, bool> + { + bool operator() (Client& c) + { + return (c.done() != 0 || c.age() > 30); + } + }; + + int getMaxFD(); + void generateFDset(fd_set *tmpFDs); + //returns 0 on timeout, otherwise 1 + int waitForSomethingToHappen(fd_set *tmpFDs); + void scan(); + void addClient(int socketFD); + void checkClientsAndPipes(fd_set *tmpFDs); + int readDataFromFD(int fd); + int processScanResults(); + int findServerServer(); + void getListFromServerServer(int address); + void enableServerServer(int on); + void serveAndClean(); + void answerBroadcast(); + void adjustRefreshTime(int serverServer); + + MyString m_extraConfigFile; + int m_configStyle; + MyString getConfigFileName(); + MyString m_usedConfigFileName; +}; +#endif diff --git a/lanbrowsing/lisa/netscanner.cpp b/lanbrowsing/lisa/netscanner.cpp new file mode 100644 index 00000000..acf0515c --- /dev/null +++ b/lanbrowsing/lisa/netscanner.cpp @@ -0,0 +1,663 @@ +/* netscanner.cpp + * + * Copyright (c) 2000, Alexander Neundorf, + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "config.h" +#include "netscanner.h" +#include "ipaddress.h" +#include "lisadefines.h" +#include <iostream> + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif +#define LISA_DEBUG 0 + +#ifdef dcerr +#undef dcerr +#endif + +#ifdef mdcerr +#undef mdcerr +#endif + +#define dcerr if (LISA_DEBUG==1) std::cerr<<"NetScanner::" +#define mdcerr if (LISA_DEBUG==1) std::cerr<<procId<<" NetScanner::" + +#include <stdio.h> +#include <sys/socket.h> +#include <sys/types.h> +#ifdef __osf__ +#undef BYTE_ORDER +#define _OSF_SOURCE +#undef _MACHINE_ENDIAN_H_ +#undef __STDC__ +#define __STDC__ 0 +#include <netinet/ip.h> +#undef __STDC__ +#define __STDC__ 1 +#endif +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +struct ICMPEchoRequest +{ + unsigned char type; + unsigned char code; + unsigned short int checkSum; + unsigned short id; + unsigned short seqNumber; +}; + +unsigned short in_cksum(unsigned short *addr, int len) +{ + int nleft = len; + int sum(0); + unsigned short *w = addr; + unsigned short answer = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), we add + * sequential 16 bit words to it, and at the end, fold back all the + * carry bits from the top 16 bits into the lower 16 bits. + */ + while (nleft > 1) + { + sum += *w++; + nleft -= 2; + } + + /* 4mop up an odd byte, if necessary */ + if (nleft == 1) + { + *(unsigned char *)(&answer) = *(unsigned char *)w ; + sum += answer; + } + + /* 4add back carry outs from top 16 bits to low 16 bits */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return(answer); +} + + +NetScanner::NetScanner(int& rawSocketFD, int strictMode) +:procId("") +,m_firstWait(5) +,m_secondWait(15) +,m_strictMode(strictMode) +,m_rawSocketFD(rawSocketFD) +,validator() +,ipRangeStr(";") +,m_maxPings(256) +,m_deliverUnnamedHosts(0) +,m_useNmblookup(0) +,hostList(0) +,tmpIPRange("") +{} + +NetScanner::~NetScanner() +{ +// std::cerr<<"----------- Netscanner dtor "<<std::endl; + delete hostList; + ::close(m_rawSocketFD); +} + +void addMissingSemicolon(MyString& text) +{ + if (text.isEmpty()) return; + if (text[text.length()-1]!=';') + text+=';'; +} + +void NetScanner::configure(Config& config) +{ + //ranges are not allowed in strict mode + if (!m_strictMode) + { + ipRangeStr=stripWhiteSpace(config.getEntry("PingAddresses","")); + addMissingSemicolon(ipRangeStr); + } + MyString pingNames=stripWhiteSpace(config.getEntry("PingNames","")); + addMissingSemicolon(pingNames); + MyString nextName; + int semicolonPos=pingNames.find(';'); + int hostsAdded(0); + while (semicolonPos!=-1) + { + nextName=pingNames.left(semicolonPos); + mdcerr<<"configure(): looking up -"<<nextName<<"-"<<std::endl; + //now the name lookup + in_addr server_addr; + hostent *hp=gethostbyname(nextName.data()); + if (hp!=0) + { + if ((m_strictMode) && (hostsAdded>=STRICTMODEMAXHOSTS)) + break; + memcpy(&server_addr, hp->h_addr, sizeof(server_addr)); + char *ip=inet_ntoa(server_addr); + mdcerr<<"configure(): looking up "<<nextName<<" gives -"<<ip<<"-"<<std::endl; + ipRangeStr=ipRangeStr+ip+';'; + hostsAdded++; + } + + pingNames=pingNames.mid(semicolonPos+1); + semicolonPos=pingNames.find(';'); + } + if ((!ipRangeStr.isEmpty()) && (ipRangeStr[0]==';')) + ipRangeStr=ipRangeStr.mid(1); + m_deliverUnnamedHosts=config.getEntry("DeliverUnnamedHosts",0); + m_useNmblookup=config.getEntry("SearchUsingNmblookup",0); + m_maxPings=config.getEntry("MaxPingsAtOnce",256); + m_firstWait=config.getEntry("FirstWait",5); + m_secondWait=config.getEntry("SecondWait",15); + if (m_firstWait<1) m_firstWait=1; + if (m_maxPings<8) m_maxPings=8; + if (m_maxPings>1024) m_maxPings=1024; + //on some systems (Solaris ?) select() doesn't work correctly + // if the microseconds are more than 1.000.000 + if (m_firstWait>99) m_firstWait=99; + if (m_secondWait>99) m_secondWait=99; + mdcerr<<"configure(): "<<ipRangeStr<<std::endl; +} + +struct in_addr NetScanner::getIPfromArray(unsigned int index) +{ + //mdcerr<<std::endl<<"*** start ***"<<std::endl; + unsigned int tmpIndex(0),indexLeft(index); + resetIPRange(); + MyString tmp(getNextIPRange()); +// mdcerr<<"NetScanner::getIPFromArray: -"<<tmp<<"-"<<std::endl; + while (!tmp.isEmpty()) + { + if (tmp.contains('/')) + { + //mdcerr<<"net/mask combination detected"<<std::endl; + MyString netStr(tmp.left(tmp.find("/"))); + MyString maskStr(tmp.mid(tmp.find("/")+1)); + unsigned int mask(IPAddress(maskStr).asInt()); + unsigned int net(IPAddress(netStr).asInt()&mask); + if ((~mask)<indexLeft) + { + indexLeft=indexLeft-(~mask+1); + tmpIndex+=(~mask)+1; + //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; + } + else + { + net+=indexLeft; + return IPAddress(net).asStruct(); + //return string2Struct(ipInt2String(net)); + } + } + else if (tmp.contains('-')==1) + { + //mdcerr<<"single range detected"<<std::endl; + MyString fromIPStr(tmp.left(tmp.find("-"))); + MyString toIPStr(tmp.mid(tmp.find("-")+1)); + //mdcerr<<"fromIPStr: "<<fromIPStr<<std::endl; + //mdcerr<<"toIPStr: "<<toIPStr<<std::endl; + unsigned int fromIP(IPAddress(fromIPStr).asInt()); + unsigned int toIP(IPAddress(toIPStr).asInt()); + //unsigned int fromIP(ipString2Int(fromIPStr)), toIP(ipString2Int(toIPStr)); + //index hinter diesem bereich + if ((fromIP+indexLeft)>toIP) + { + tmpIndex+=1+toIP-fromIP; + indexLeft=indexLeft-(1+toIP-fromIP); + //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; + } + //index in diesem bereich + else + { + fromIP+=indexLeft; + return IPAddress(fromIP).asStruct(); + //return string2Struct(ipInt2String(fromIP)); + } + + } + else if (tmp.contains('-')==4) + { + //mdcerr<<"multiple range detected"<<std::endl; + int cp(tmp.find('-')); + int from1(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + cp=tmp.find('.'); + int to1(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + cp=tmp.find('-'); + int from2(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + cp=tmp.find('.'); + int to2(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + cp=tmp.find('-'); + int from3(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + cp=tmp.find('.'); + int to3(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + cp=tmp.find('-'); + int from4(atoi(tmp.left(cp).data())); + tmp=tmp.mid(cp+1); + + int to4(atoi(tmp.data())); + + unsigned int count((1+to4-from4)*(1+to3-from3)*(1+to2-from2)*(1+to1-from1)); + if (count<indexLeft) + { + tmpIndex+=count; + indexLeft-=count; + //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; + } + else + { + for (int b1=from1; b1<=to1; b1++) + for (int b2=from2; b2<=to2; b2++) + for (int b3=from3; b3<=to3; b3++) + for (int b4=from4; b4<=to4; b4++) + { + if (tmpIndex==index) + { + return IPAddress(b1,b2,b3,b4).asStruct(); + }; + tmpIndex++; + indexLeft--; + //mdcerr<<"i: "<<tmpIndex<<" left:"<<indexLeft<<std::endl; + } + } + } + //single IP address + else if (tmp.contains('.')==3) + { + //mdcerr<<"single IP address detected"<<std::endl; + //if (tmpIndex==index) return string2Struct(tmp); + if (tmpIndex==index) return IPAddress(tmp).asStruct(); + else + { + tmpIndex++; + indexLeft--; + //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; + } + } + //mdcerr<<"nextIPRange: *"<<tmp<<"*"<<std::endl; + tmp=getNextIPRange(); + } + return IPAddress("0.0.0.0").asStruct(); +} + +void NetScanner::resetIPRange() +{ + tmpIPRange=ipRangeStr; +} + +MyString NetScanner::getNextIPRange() +{ + if (tmpIPRange.contains(';')<1) return ""; + int cp(tmpIPRange.find(';')); + MyString tmp(tmpIPRange.left(cp)); + tmpIPRange=tmpIPRange.mid(cp+1); + return tmp; +} + +char* NetScanner::ip2Name(struct in_addr ip) +{ + struct hostent *hostname=0; + // Set the hostname of node + if ( ( hostname = gethostbyaddr( (char *) &ip.s_addr, 4, AF_INET ) ) == 0 ) + { + mdcerr << "ip2Name gethostbyname* error" << std::endl; + return inet_ntoa( ip ); + } + mdcerr<<"ip2name -"<<hostname->h_name<<std::endl; + return hostname->h_name; +} + +void NetScanner::nmblookupScan(std::list<Node>* newList) +{ + mdcerr<<"nmblookupScan()"<<std::endl; + //newList->clear(); + FILE * nmblookupFile=popen("nmblookup \"*\"","r"); + //no success + if (nmblookupFile==0) + { + mdcerr<<"nmblookupScan(): could not start nmblookup"<<std::endl; + return; + }; + MyString dummy(""); + char *receiveBuffer=0; + int bufferSize(0); + int nmblookupFd=fileno(nmblookupFile); + struct timeval tv; + fd_set fds; + int done(0); + int timeOuts(0); + char *tmpBuf=new char[16*1024]; + do + { + FD_ZERO(&fds); + FD_SET(nmblookupFd,&fds); + tv.tv_sec=10; + tv.tv_usec=0; + int result=select(nmblookupFd+1,&fds,0,0,&tv); + //error + if (result<0) + done=1; + //timeout + else if (result==0) + { + timeOuts++; + //3 time outs make 30 seconds, this should be *much* more than enough + if (timeOuts>=3) + done=1; + } + else if (result>0) + { + //read stuff + int bytesRead=::read(nmblookupFd,tmpBuf,16*1024); + //pipe closed + if (bytesRead==0) + done=1; + else + { + char *newBuf=new char[bufferSize+bytesRead]; + if (receiveBuffer!=0) + { + memcpy(newBuf,receiveBuffer,bufferSize); + delete [] receiveBuffer; + } + memcpy(newBuf+bufferSize,tmpBuf,bytesRead); + receiveBuffer=newBuf; + bufferSize+=bytesRead; + } + } + } while (!done); + + // Warning: The return value of pclose may be incorrect due to the + // SIGCHLD handler that is installed. Ignore it! + pclose(nmblookupFile); + + delete [] tmpBuf; + //we received nothing + if (receiveBuffer==0) + return; + + //check for a terrminating '\0' + if (receiveBuffer[bufferSize-1]=='\0') + receiveBuffer[bufferSize-1]='\0'; + + //std::cerr<<receiveBuffer<<std::endl; + + tmpBuf=receiveBuffer; + int bytesLeft=bufferSize; + + int bufferState=0; + while (bytesLeft>0) + { + //mdcerr<<"bytesLeft: "<<bytesLeft<<" received: "<<bufferSize<<std::endl; + //since we added a terminating \0 we can be sure here + char *endOfLine=(char*)memchr(tmpBuf,'\n',bytesLeft); + //point to the last character + if (endOfLine==0) + endOfLine=receiveBuffer+bufferSize-1; + + //0-terminate the string + *endOfLine='\0'; + //now tmpBuf to endOfLine is a 0-terminated string + int strLength=strlen(tmpBuf); + //hmm, if this happens, there must be something really wrong + if (strLength>1000) + break; + + bytesLeft=bytesLeft-strLength-1; + + if (bufferState==0) + { + if (isdigit(tmpBuf[0])) + bufferState=1; + } + //yes, no else ! + if (bufferState==1) + { + char tmpIP[1024]; + //std::cerr<<"tmpBuf: -"<<tmpBuf<<"-"<<std::endl; + if (sscanf(tmpBuf,"%s *<00>\n",tmpIP) == 1) { + mdcerr<<"nmblookupScan: tmpIP: -"<<tmpIP<<"-"<<std::endl; + struct sockaddr_in addr; + if ((addr.sin_addr.s_addr = inet_addr(tmpIP)) != INADDR_NONE) + if (!hostAlreadyInList(addr.sin_addr.s_addr,newList)) + { + if (validator.isValid(addr.sin_addr.s_addr)) + { + mdcerr<<"nmblookupScan: adding "<<inet_ntoa(addr.sin_addr)<<std::endl; + newList->push_back(Node(dummy,addr.sin_addr.s_addr)); + } + } + } + } + tmpBuf=endOfLine+1; + }; + mdcerr<<"nmblookupScan: finished"<<std::endl; + delete [] receiveBuffer; +} + +void NetScanner::pingScan(std::list<Node>* newList) //the ping-version +{ + mdcerr<<"pingScan()"<<std::endl; + //newList->clear(); + MyString dummy(""); + mdcerr<<"pingScan: m_maxPings: "<<m_maxPings<<std::endl; + + pid_t pid=getpid(); + ICMPEchoRequest echo; + echo.type=ICMP_ECHO; + echo.code=0; + echo.id=pid; + echo.seqNumber=0; + echo.checkSum=0; + echo.checkSum=in_cksum((unsigned short *)&echo,8); + + char receiveBuf[16*1024]; + //before we start first read everything what might be in the receive buffer + //of the raw socket + + timeval tv1; + //wait a moment for answers + tv1.tv_sec = 0; + tv1.tv_usec = 0; + fd_set clearSet; + FD_ZERO(&clearSet); + FD_SET(m_rawSocketFD,&clearSet); + while(select(m_rawSocketFD,&clearSet,0,0,&tv1)>0) + { + ::recvfrom(m_rawSocketFD,(char*)&receiveBuf,16*1024,0,0,0); + tv1.tv_sec = 0; + tv1.tv_usec = 0; + FD_ZERO(&clearSet); + FD_SET(m_rawSocketFD,&clearSet); + } + //now the buffer should be empty + + //wait a moment for answers + tv1.tv_sec = 0; + tv1.tv_usec = m_firstWait*10*1000;//0.5 sec + + int loopCount(2); + if (m_secondWait<0) + loopCount=1; + for (int repeatOnce=0; repeatOnce<loopCount; repeatOnce++) + { + mdcerr<<"******************** starting loop *****************"<<std::endl; + unsigned int current(0); + int finished(0); + while (!finished) + { + for (int con=0; con<m_maxPings; con++) + { + struct in_addr tmpIP; + do + { + tmpIP=getIPfromArray(current); + current++; +// mdcerr<<"pingScan(): trying "<<inet_ntoa(tmpIP)<<std::endl; + if (hostAlreadyInList(tmpIP.s_addr,newList)) + mdcerr<<"already in list :-)"<<std::endl; + if (!validator.isValid(tmpIP.s_addr)) + mdcerr<<"pingScan(): invalid IP :-("<<std::endl; + //ping only hosts which are allowed to receive the results + //and which are not in the list + } while ( (tmpIP.s_addr!=0) + && ((!validator.isValid(tmpIP.s_addr)) + || (hostAlreadyInList(tmpIP.s_addr,newList)))); + + finished=(tmpIP.s_addr==0); + if (!finished) + { + //send the icmp echo request + struct sockaddr_in toAddr; + toAddr.sin_family = AF_INET; + toAddr.sin_addr = tmpIP; + toAddr.sin_port = 0; + (void)sendto(m_rawSocketFD,(char*)&echo,sizeof(echo),0,(sockaddr*)&toAddr,sizeof(toAddr)); + //int sb=sendto(sockFD,(char*)&echo,sizeof(echo),0,(sockaddr*)&toAddr,sizeof(toAddr)); +// mdcerr<<"pingScan: pinging "<<inet_ntoa(toAddr.sin_addr)<<std::endl; + } + else break; + } + select(0,0,0,0,&tv1); + //now read the answers, hopefully + struct sockaddr_in fromAddr; + socklen_t length(sizeof(fromAddr)); + int received(0); + + fd_set sockFDset; + FD_ZERO(&sockFDset); + FD_SET(m_rawSocketFD,&sockFDset); + tv1.tv_sec=0; + tv1.tv_usec=0; + while(select(m_rawSocketFD+1,&sockFDset,0,0,&tv1)>0) + { + received=recvfrom(m_rawSocketFD, (char*)&receiveBuf, 16*1024, 0, + (sockaddr*)&fromAddr, &length); + if (received!=-1) + { +// mdcerr<<"pingScan: received from "<<inet_ntoa(fromAddr.sin_addr)<<" "<<received<<" b, "; + struct ip *ipFrame=(ip*)&receiveBuf; + int icmpOffset=(ipFrame->ip_hl)*4; + icmp *recIcmpFrame=(icmp*)(receiveBuf+icmpOffset); + int iType=recIcmpFrame->icmp_type; + //if its a ICMP echo reply + if ((iType==ICMP_ECHOREPLY) + //to an echo request we sent + && (recIcmpFrame->icmp_id==pid) + //and the host is not yet in the list + && (!hostAlreadyInList(fromAddr.sin_addr.s_addr,newList))) + { + //this is an answer to our request :-) +// mdcerr<<"pingScan: adding "<<inet_ntoa(fromAddr.sin_addr)<<std::endl; + newList->push_back(Node(dummy,fromAddr.sin_addr.s_addr)); + } + } + tv1.tv_sec=0; + tv1.tv_usec=0; + FD_ZERO(&sockFDset); + FD_SET(m_rawSocketFD,&sockFDset); + //FD_SET(sockFD,&sockFDset); + } + } + tv1.tv_sec = 0; + tv1.tv_usec = m_secondWait*10*1000;//0.5 sec + } + mdcerr<<"pingScan() ends"<<std::endl; +} + +void NetScanner::doScan() +{ + mdcerr<<"doScan"<<std::endl; + //child + std::list<Node>* tmpPingList=new std::list<Node>; + mdcerr<<"doScan: created list"<<std::endl; + if (m_useNmblookup) + nmblookupScan(tmpPingList); + pingScan(tmpPingList); + // get the names from cache or lookup + completeNames(tmpPingList); + mdcerr<<"doScan: completed names"<<std::endl; + if (m_deliverUnnamedHosts==0) + removeUnnamedHosts(tmpPingList); + + mdcerr<<"doScan: added hosts"<<std::endl; + + delete hostList; + hostList=tmpPingList; +} + +int NetScanner::hostAlreadyInList(int ip, std::list<Node>* nodes) +{ + for (std::list<Node>::iterator node = nodes->begin(); node != nodes->end(); node++) + { + if (node->ip==ip) + return 1; + } + return 0; +} + +void NetScanner::removeUnnamedHosts(std::list<Node>* nodes) +{ + nodes->remove_if(is_unnamed_node()); +} + +void NetScanner::completeNames(std::list<Node>* nodes) +{ + struct sockaddr_in tmpAddr; + //for every host + for (std::list<Node>::iterator node = nodes->begin(); node != nodes->end(); node++) + { + tmpAddr.sin_addr.s_addr=node->ip; + mdcerr<<"completeNames: looking up "<<inet_ntoa(tmpAddr.sin_addr)<<std::endl; + int done(0); + //first look wether we have the name already + if (hostList!=0) for (std::list<Node>::iterator oldNode=hostList->begin(); oldNode!=hostList->end(); oldNode++) + { + if (node->ip==oldNode->ip) + { + mdcerr<<"completeNames: cached: "<<oldNode->name<<" :-)"<<std::endl; + node->name=oldNode->name; + done=1; + break; + } + } + //otherwise do a name lookup + if (!done) + { + //IPAddress tmpAddress(node->ip); + //mdcerr<<"NetScanner::completeNames: doing actual lookup"<<std::endl; + node->name=ip2Name(tmpAddr.sin_addr); + mdcerr<<"completeNames: resolved: "<<node->name<<std::endl; + } + } +} + diff --git a/lanbrowsing/lisa/netscanner.h b/lanbrowsing/lisa/netscanner.h new file mode 100644 index 00000000..a93bf3ee --- /dev/null +++ b/lanbrowsing/lisa/netscanner.h @@ -0,0 +1,102 @@ +/* netscanner.h + * + * Copyright (c) 1998, 1999, 2000 Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef NETSCANNER_H +#define NETSCANNER_H + +#include <list> + +#include "mystring.h" +#include "addressvalidator.h" +#include "tcpnode.h" +#include "configfile.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <time.h> + +#include <arpa/inet.h> +#include <errno.h> +#include <netdb.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/time.h> + +#include <stdio.h> + +class NetScanner +{ + public: + NetScanner(int& rawSocket, int strictMode); + ~NetScanner(); + + void doScan(); + + void completeNames(std::list<Node>* nodes); + + //using nmblookup to determine the ip-address + //MyString ip2NetbiosName(const MyString& target, MyString& groupName); + + char* ip2Name(struct in_addr ip); + + MyString procId; + int m_firstWait; + int m_secondWait; + void configure(Config& config); + protected: + int m_strictMode; + int& m_rawSocketFD; + AddressValidator validator; + MyString ipRangeStr; + MyString nameMask; + + int m_maxPings; + int m_deliverUnnamedHosts; + int m_useNmblookup; + //return ip-address with number index from a virtual array + //with all ip-addresses according to ipRangeStr + struct in_addr getIPfromArray(unsigned int index); + + std::list<Node>* hostList; + + //needed for getIPfromArray() + //contains the part of ipRangeStr, which is not yet parsed + MyString tmpIPRange; + //has to be called before every first call of getNextIPRange + void resetIPRange(); + //returns the next range/part from tmpIPRange before the next semicolon + MyString getNextIPRange(); + + void pingScan(std::list<Node>* newList); //the ping-version + void nmblookupScan(std::list<Node>* newList); //the nmblookup "*"-version + int hostAlreadyInList(int ip, std::list<Node>* nodes); + void removeUnnamedHosts(std::list<Node>* nodes); + + struct is_unnamed_node : std::unary_function<Node&, bool> + { + bool operator() (Node& n) + { + struct in_addr tmpAddr; + tmpAddr.s_addr = n.ip; + return (strcmp(inet_ntoa(tmpAddr), n.name.data()) == 0); + } + }; +}; + +#endif diff --git a/lanbrowsing/lisa/strictmain.cpp b/lanbrowsing/lisa/strictmain.cpp new file mode 100644 index 00000000..a948d6df --- /dev/null +++ b/lanbrowsing/lisa/strictmain.cpp @@ -0,0 +1,261 @@ +/* strictmain.cpp + * + * Copyright (c) 1998-2000 Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#include "lisadefines.h" +#include "netmanager.h" + +#include <iostream> +#include <signal.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> + +// detect linux/glibc for the gnu style --args +#if defined(__linux__) || defined(__linux) || defined(linux) +# include <features.h> +# ifdef __GLIBC__ +// only gnu libc has getopt.h... getopt(3) is defined to be in unistd.h +// by POSIX.2 +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +# include <getopt.h> +# endif // __GLIBC__ +# define GNU_GETOPT +#endif // linux + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif +#define LISA_DEBUG 0 + +#ifdef dcerr +#undef dcerr +#endif + +#define dcerr if (LISA_DEBUG==1) std::cerr<<"strictmain " + +void printVersion() +{ + const char * versionInfo=\ + "\r\nThis is the restricted LAN Information Server resLISa "MYVERSION"\r\n"\ + "It is free software according the GNU General Public License\r\n"\ + "Copyright (c) 2000-2003 by Alexander Neundorf\r\n"\ + "email: neundorf@kde.org\r\n"; + std::cout<<versionInfo<<std::endl; +} + +void usage() +{ + printVersion(); + const char * usageInfo=\ + "-v, --version prints out a short version info\n"\ + "-u, --unix deprecated\n"\ + "-k, --kde1 deprecated\n"\ + "-K, --kde2 deprecated\n"\ + " reslisa now always looks first for $(HOME)/.reslisarc, then for /etc/reslisarc\"\n"\ + "-c, --config=FILE read this and no other configuration file\n"\ + "-q, --quiet start quiet without the greeting message\n"\ + "-h, --help you are currently reading it ;-)\n"; + std::cout<<usageInfo<<std::endl; +//I thought this would be the way to check wether long options are supported... +//#ifndef _GNU_SOURCE +// cout<<"Please note that the long options are not supported on this system"<<endl; +//#endif +} + +void destruct(int sigNumber) +{ + signal(sigNumber,SIG_IGN); + std::cout<<"signal caught: "<<sigNumber<<", exiting"<<std::endl; + //signal(sigNumber,&destruct); + exit(0); +} + +NetManager *manager(0); + +void readConfig(int sigNumber) +{ + std::cout<<"readConfig(): signal caught: "<<sigNumber<<std::endl; + signal(SIGHUP,SIG_IGN); + if (manager!=0) + manager->readConfig(); + signal(SIGHUP,&readConfig); +} + +void printState(int sigNumber) +{ + std::cout<<"printState(): signal caught: "<<sigNumber<<std::endl; + signal(SIGUSR1,SIG_IGN); + if (manager!=0) + manager->printState(); + signal(SIGUSR1,&printState); +} + +void setSignalHandler() +{ + signal(SIGHUP,&readConfig); + signal(SIGUSR1,&printState); + + signal(SIGINT,&destruct); + signal(SIGQUIT,&destruct); + signal(SIGILL,&destruct); + signal(SIGTRAP,&destruct); + signal(SIGABRT,&destruct); + signal(SIGBUS,&destruct); + signal(SIGSEGV,&destruct); + signal(SIGUSR2,&destruct); + signal(SIGPIPE,&destruct); + signal(SIGALRM,&destruct); + signal(SIGTERM,&destruct); + signal(SIGFPE,&destruct); +#ifdef SIGPOLL + signal(SIGPOLL, &destruct); +#endif +#ifdef SIGSYS + signal(SIGSYS, &destruct); +#endif +#ifdef SIGVTALRM + signal(SIGVTALRM, &destruct); +#endif +#ifdef SIGXCPU + signal(SIGXCPU, &destruct); +#endif +#ifdef SIGXFSZ + signal(SIGXFSZ, &destruct); +#endif +} + +#ifdef GNU_GETOPT +static struct option const long_opts[] = +{ + {"version", no_argument, 0, 'v'}, + {"quiet", no_argument, 0, 'q'}, + {"unix", no_argument, 0, 'u'}, + {"kde1", no_argument, 0, 'k'}, + {"kde2", no_argument, 0, 'K'}, + {"config", required_argument, 0, 'c'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} +}; +#endif + +int main(int argc, char** argv) +{ + int quiet(0); + int c(0); + int configStyle(UNIXCONFIGSTYLE); + MyString configFile; + int portToUse(MYPORT); + +//I thought this would be the way to check wether long options are supported... +#ifdef GNU_GETOPT + while ((c=getopt_long(argc, argv, "vqukKc:h", long_opts, 0))!=-1) +#else + while ((c=getopt(argc, argv, "vqukKc:h"))!=-1) +#endif + { + switch (c) + { + case 0: + break; + case 'v': + printVersion(); + exit(0); + break; + case 'q': + quiet=1; + break; + case 'u': + case 'k': + case 'K': + std::cerr<<"\a\nThe command line switches -k, -K, -u and \ntheir long versions "\ + "--kde1, --kde2 and --unix are not supported anymore.\n"\ + "ResLisa will always first look for $(HOME)/.reslisarc , then for /etc/reslisarc.\n"\ + "If your lisa configuration file was created using an older version of \n"\ + "the KDE control center, copy the $(HOME)/.kde/share/config/reslisarc to $(HOME)/.reslisarc.\n"<<std::endl; + break; + + case 'c': + configFile = optarg; + configStyle = EXTRACONFIGSTYLE; + break; + + case 'h': + default: + usage(); + exit(0); + break; + } + } + + //fork and let the parent exit + pid_t pid=fork(); + if (pid>0) + { + //this is the parent + exit(0); + } + else if (pid<0) + { + dcerr<<"could not fork()"<<std::endl; + exit(0); + } + //we will only read/write to/from this socket in the child process + int rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); + if (rawSocket==-1) + { + std::cout<<"could not create raw socket, root privileges required"<<std::endl; + std::cout<<"take a look at the README for more information"<<std::endl; + exit(0); + } + int bufferSize(60*1024); + int on(1); + setsockopt(rawSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, + sizeof(bufferSize)); + int result=setsockopt(rawSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on, + sizeof(on)); + dcerr<<"setsockopt returns "<<result<<std::endl; + //dropping root privileges + //they will be regained once in the child process + //for creating a raw socket + + //now dropping root privileges once and ever + setuid(getuid()); + + //according to R. Stevens the following three lines + //make daemons feel good :) + setsid(); + chdir("/"); + umask(0); + + dcerr<<"starting, dropped root privileges"<<std::endl; + dcerr<<"port: "<<portToUse<<" file: "<<configFile<<std::endl; + NetManager netmanager(rawSocket,portToUse,configFile,configStyle,1); + manager=&netmanager; + dcerr<<"NetManager created"<<std::endl; + setSignalHandler(); + + netmanager.readConfig(); + if (netmanager.prepare()) + { + if (!quiet) + printVersion(); + netmanager.run(); + } + dcerr<<"server finished"<<std::endl; +} diff --git a/lanbrowsing/lisa/stringlist.cpp b/lanbrowsing/lisa/stringlist.cpp new file mode 100644 index 00000000..3bf82f9c --- /dev/null +++ b/lanbrowsing/lisa/stringlist.cpp @@ -0,0 +1,110 @@ +#include "simplelist.h" + +template <class T> +SimpleList<T>::SimpleList() + :m_list(0) + ,m_current(0) + ,m_last(0) + ,m_size(0) +{}; + +template <class T> +SimpleList<T>::~SimpleList() +{ + clear(); +}; + +template <class T> +void SimpleList<T>::append(const T& text) +{ + if (m_list==0) + { + m_list=new TemplNode<T>(text); + m_last=m_list; + } + else + { + m_last->m_next=new TemplNode<T>(text); + m_last=m_last->m_next; + }; + m_size++; +}; + +template <class T> +void SimpleList<T>::removeFirst() +{ + if (m_list==0) return; + TemplNode<T> *first=m_list; + m_list=m_list->m_next; + m_size--; + if (m_list==0) + m_last=0; + m_current=0; + delete first; +}; + +template <class T> +void SimpleList<T>::clear() +{ + while (m_list!=0) + removeFirst(); + m_current=0; + m_last=0; + m_list=0; + m_size=0; +}; + +template <class T> +void SimpleList<T>::remove(T* item) +{ + if (item==0) return; + TemplNode<T>* pre(0); + for (T* tmp=first(); tmp!=0; tmp=next()) + { + if (tmp==item) + { + if (m_current==m_list) + { + removeFirst(); + return; + } + else + { + TemplNode<T> *succ=m_current->m_next; + if (m_current==m_last) + m_last=pre; + delete m_current; + pre->m_next=succ; + m_size--; + m_current=0; + + }; + }; + pre=m_current; + }; + +}; + +template <class T> +T* SimpleList<T>::first() +{ + m_current=m_list; + if (m_list==0) + return 0; + return &m_current->m_item; +}; + +template <class T> +T* SimpleList<T>::next() +{ + if (m_current==0) return 0; + m_current=m_current->m_next; + if (m_current==0) return 0; + return &m_current->m_item; +}; + +template <class T> +int SimpleList<T>::size() +{ + return m_size; +} diff --git a/lanbrowsing/lisa/stringlist.h b/lanbrowsing/lisa/stringlist.h new file mode 100644 index 00000000..44f0d0d3 --- /dev/null +++ b/lanbrowsing/lisa/stringlist.h @@ -0,0 +1,40 @@ +#ifndef SIMPLELIST_H +#define SIMPLELIST_H + +#include "mystring.h" + +template <class T> +struct TemplNode +{ + TemplNode(const T& text) + :m_item(text),m_next(0) {}; + T m_item; + TemplNode* m_next; +}; + + +template <class T> +class SimpleList +{ + public: + SimpleList(); + ~SimpleList(); + void append(const T& item); + void clear(); + int size(); + T* first(); + T* next(); + void removeFirst(); + void remove(T* item); + protected: + TemplNode<T>* m_list; + TemplNode<T>* m_current; + TemplNode<T>* m_last; + int m_size; +}; + + +template class SimpleList<int>; + +#endif + diff --git a/lanbrowsing/lisa/tcpnode.h b/lanbrowsing/lisa/tcpnode.h new file mode 100644 index 00000000..394ca928 --- /dev/null +++ b/lanbrowsing/lisa/tcpnode.h @@ -0,0 +1,29 @@ +/* tcpnode.h + * + * Copyright (c) 2000 Alexander Neundorf + * neundorf@kde.org + * + * You may distribute under the terms of the GNU General Public + * License as specified in the COPYING file. + * + * 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. + * + */ + +#ifndef _TCPNODE_H_ +#define _TCPNODE_H_ + +#include "mystring.h" + +struct Node +{ + Node(const MyString& n, int ipAddress) + :name(n),ip(ipAddress) {}; + MyString name; + unsigned int ip; +}; + +#endif |