wsinetd - Windows Simple inetd daemon

INTRODUCTION

wsinetd's ambition is to be the Windows NT's equivalent of the standard Unix inetd super daemon.

The wsinetd project is hosted at SourceForge, is maintained by Denis Bernard <wsinetd@wildservices.net> and is released under the terms of the GNU General Public License (see the COPYRIGHT section below).

For those who don't even know what is inetd (Internet Daemon):

"It listens for connections on certain internet sockets. When a connection is found on one of its sockets, it decides what service the socket corresponds to, and invokes a program to service the request. After the program is finished, it continues to listen on the socket. Essentially, inetd allows running one daemon to invoke several others, reducing load on the system." (excerpt from the inetd manpage, author unknown)

Supported platforms

wsinetd runs as a Windows NT service under LocalSystem account, allowing for user authentication and impersonation by the invoked servers.

It is designed to run on Windows NT and its derivates: Windows 2000, and XP. The core module (socket listening and pipe server) should be portable to Windows 9x/Me without changes in the core's code, but since I didn't see the point in running such a software on Windows 9x, I've choosen not to support these platforms. Having said that, any contributions are welcome (see the developpers notes section).

TECHNICAL INFOS (IMPORTANT READING !)

The invoked programs read commands from their stdin and answer to their stdout with standard file I/O. It doesn't work with programs that expect a socket descriptor for file I/O since unsing sockets as files isn't fully supported under Windows (i.e. using it with Cygwin's inetutils won't work).

That's why this program was written: many unix server software are portable to Windows, but they often need a "wrapper" to start them on a pre-established connection with a client (as does inetd under unix); if the server software doesn't perform socket specific operations on its stdio, wsinetd will do the job.

wsinetd can also be used to create Windows native servers without worrying about Windows Sockets programming: in your server, everything you have to do (beside the server specific logic) is to read the client's commands from stdin and output answers to stdout ! Implementing an Internet server is now so simple ;)

DO NOT USE wsinetd IN PRODUCTION ENVIRONMENTS - THIS IS STILL PRE-ALPHA SOFTWARE !!!

Actually, the sole protection resides in the possible limitation of the number of concurent connections; but unlike inetd, the number of connections in the last minute isn't monitored. Anyway, there is an hardcoded limitation of 2048 concurent connections per listened port. I haven't been able to test it yet, but I'm pretty sure that any big server will be on its knees before reaching this limit.

You should also be aware that the invoked servers will run as LocalSystem, this means that if the server doesn't authenticate and impersonate the remote user upon connection (like any pop/imap/ftp/etc. server does), depending of the server software functionalities and/or holes, the remote user will be able to do lots of nasty things on your machine... Have a look at the sample configuration file below and you'll see what can be done...

LIMITATIONS

See the TECHNICAL INFOS

GETTING wsinetd

wsinetd is downloadable from the download page hosted by SourceForge.

BUILDING wsinetd

Microsoft Visual C++ 6.0 (sp3 ?) is required to build from the provided project files. Anyway, porting to older versions of MSVC should be easy, as well as porting to other Windows C compilers.

Load wsinetd.dsw in MSVC and compile the msgdll sub-project first. This will create wsinetd.dll and eventlog.h (required to compile the main executable). This dll contains message templates for NT's event log.

Then compile the main project, wsinetd. Note that to compile for Windows NT 4, you should change the pre-processor options _WIN32_WINNT and WINVER from 0x0500 to 0x0400.

CONFIGURATION FILE

In the configuration file, lines beginning with a # or left blank are ignored. Each server is defined on a separate line:

<port> <max_connections> <path_to_executable> <command_line>

where:

<port>
is the port name or number to listen to. If you use a name, it must be defined in %SystemRoot%\System32\drivers\etc\services
<max_connections>
is the maximum concurent client connections allowed for this port. Set it to -1 to disable the limit (there is anyway an hard coded limit of 2048).
<path_to_executable>
is the file name with full qualified path of the server's executable (for example: C:\progra~1\myserver\server.exe). Do not forget the file extension ! If you want to use file names containing spaces, enclose them between double quotes.
<command_line>
the full command line. The first arg will be read as argv[0] (the executable name and/or path). Enclose arguments containing spaces between double quotes.

Here is a sample config file:

# sample config file

# pop2-3 server (the popd daemon uses the pop2 protocol when started as pop2 ...
#                and pop3 when started as pop3)
pop2   -1  "c:\program files\popserver\popd.exe"  pop2 --no-sockets
pop3   -1  "c:\program files\popserver\popd.exe"  pop3 --no-sockets

# imap4r1 server (no particular cmd line here), the limit is set up to comply with
# the maximum expected load on a small intranet (10 machines)
imap4  60  c:\ImapToolkit-2001a\imapd.exe

# hacky remote shell run with LocalSystem's privileges (no auth !)
# never do this, except if you REALLY KNOW what you're doing !!!
# the limit is simple: reserved use for me
12345   1  C:\winnt\system32\cmd.exe

# simple perl script used as a server...
# note that the executable path is repeated twice.
11111   5  c:\perl\bin\perl.exe c:\perl\bin\perl.exe -w "c:\My Scripts\testbed.pl"

The last line of this sample configuration file is parsed by wsinetd like this:

port
11111
max_connections
5
path_to_executable
c:\perl\bin\perl.exe
command_line
c:\perl\bin\perl.exe -w "c:\My Scripts\testbed.pl"

INSTALLING wsinetd AS A SERVICE

Copy wsinetd.exe AND wsinetd.dll AND wsinetd.conf in any folder you like, then in a shell, CD to that folder and type the command:

	wsinetd -i

and you're off !

If you want the service to start automatically upon system startup, you can do it by using NT's service control manager.

to start the service manually, type:

	net start wsinetd

to stop it, type:

	net stop wsinetd

To uninstall the service, stop it first, then type:

	wsinetd -u

NOTICE: when you change the config file, the service must be restarted.

BUGS & OTHER IMPORTANT NOTICES

Please read the TECHNICAL INFOS section once again.

I've tested wsinetd with a few servers:

The only problem I've encountered was with Microsoft Outlook 2000 and UW's imapd (native Win2k version, not the Cygwin's one): when I do a "Download all" in Outlook, the "Download window" opens, but oulook deadlocks here... if it is terminated from the task manager, the pipe-server, its child threads and the spawned process cleanup gracefully... I've tested many things but with no clue... (see developpers notes if you're interested). Anyway, it works very well with MS Outlook Express 6 or with MS Outllok 2000 + imapd in its Cygwin version...

Any reports on success/failure stories with various client-server combinations are welcome.

NOTES TO DEVELOPPERS

Contributions and/or enhancements are welcome !

You can start wsinetd with the -d option from within MSVC, wsinetd will then start in a console. It will run on your user account (no LocalSystem privileges, aren't you ?). To terminate it, hit return in the console window.

It outputs extensive debugging information to the active Windows debugger. You can use DbMon from Microsoft's Platform SDK for example.

Internal synopsis:

service_main()
	// main func, called upon service startup
	-> service_init()
		setup glocal objects
		read config file
		start a new paused listener thread for each server (entrypoint is wsinetd_main())
	starts listener threads
	waits for a stop event (triggered by the service control dispatcher)
	waits for all listeners to shutdown
	bailout

wsinetd_main()
	// listens for incoming connections
	starts a new pipe_server() thread for each accepted connection
	if stop event triggered, waits for shitdown of its child threads (pipe servers) then exits

pipe_server()
	setup pipes
	setup input thread (reads data from spawned process and sends it to connected client)
	setup output thread (reads data from connected client and sends it to spawned process)
	spawns the server process
	waits for either stop_event/input thread/output thread/spawned process and tries to
		terminate the others gracefully

About the problem with MS Outlook 2000 & imapd Win2k: I thought that Outlook was faulty, but from time to time, it worked fine or randomly...

For example, here is the old version of othread():

unsigned __stdcall  othread (void * data)
{
  STREAM *stream = data;
  char buf[1];
  long i;
  while ((i = recv (stream->sock, buf, 1, 0)) > 0 &&
	WriteFile (stream->ohdl, buf, 1, (DWORD*)&i, NULL));
  return 0;
}

which didn't work but, this one did:

...
  while ((i = recv (stream->sock, buf, 1, 0)) > 0 &&
	WriteFile (stream->ohdl, buf, 1, (DWORD*)&i, NULL) &&
	FlushFileBuffers (stream->ohdl));
...

Despite being theorically useless on byte-oriented anonymous pipes, FlushFileBuffers() seemed to do it, and Outlook never failed. I wanted to optimize a little the I/O speed and enlarged the buffer size (for byte-stream sockets, receipt of a single byte is sufficient to unblock the caller).
Now it doesn't work anymore with Outlook: This modification only changed slightly the code size (10 bytes ?), execution time (a few CPU cycles) and data alignment... I've tried lots of things, even a highly complex asynchronous I/O mechanism in the I/O threads, allowing for example non-concurent use of the client socket, but nothing helped. And worse: it works well with imapd in its Cygwin version... Who's at fault ? Outlook, wsinetd or imapd ? In most stories, the man in the middle is always the bad one, so wsinetd is my main suspect, but who knows... If you've got a clue, I'd be happy to hear from you!

Note also that Unicode support is in progress but it hasn't been tested yet (it shouldn't even compile).

Maximum concurent connections

The stack of each thread is allocated in the address space of the process. With a maximum addressable momory of 2G per process, with the settings used in wsinetd.h, we can create 2G / (128K + 64K + 64K) = 8192 maximum simultaneous client connections (1 pipe + 2 io per connection).
Note that the listener threads aren't taken in account here.
I haven't been able to test it yet, but I'm pretty sure that any big server will be on its knees before reaching this limit (even if only the first page of the stack memory is commited). Another little thing to take in account is that there's a new process created for each client connection, and Windows isn't written to run thousands of processes simultaneously...

If necessary, this limit could be pushed up by:

  1. Fine-tune the necessary stack sizes
  2. Create a separate process for each listened port
  3. Create a wrapper for the servers: added to the sources of the server and taking over the server's main(), it handles reception of the listening and first client sockets from the listener process, then acts as servers ran by the Unix inetd in wait mode (takes over socket listening and creates a new thread for each new connection, calling the server's original main())... This implies naturally that the whole server is written in a thread safe manner (no global data for example)

The second and third topics, if done together would make wsinetd itself useless. Anyway wsinetd isn't intended to run high duty servers. So, I'll probably focus only on the first and third ones.

Advices for a Windows 9x port

<TROLLMODE>Before thinking about porting to Windows 9x, you should think about installing a real OS...</TROLLMODE>
But, if you really want to, porting to Win9x should be simple. First create a separate project file (call it wsinetd9x for example), include wsinetd.c (listener/pipe-server) and util.c in this project and create a main.c or service.c that will be specific to Win9x (copy the original service.c and remove all nt's specific stuff). Then, all you have to to is to add W9x specific service management and rewrite the error log routines.

CREDITS

I wish to thank Mark Crispin, author of the University of Washinon's IMAP Toolkit, for granting me the permission to freely copy and paste huge parts of code from inetlisn (not part of the IMAP Toolkit). In fact, wsinetd is completely build upon Mark's work. I'm sorry Mark, I didn't use the Free Fork License, it was much too work for me to adapt it, and I prefer granting privileges to the final user reather than to me (I'm only speaking about this project ;)

TODO

Lots of things:

The main priority being for the moment to correct that damned bug which showed up with MS Outlook 2000 used together with UW's imapd Win2K.

COPYRIGHT

  wsinetd - Windows Simple inetd daemon

  Copyright (C) 2001-2002 Denis Bernard <wsinetd@wildservices.net>

  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

REVISON HISTORY

Note about version numbering: in version number x.y.z, x is the major version number, y is the minor one and z is the build number in the same major version.
2002/12/19 v0.3.8
  - Changed compilation options for msgdll: uses now msvcrt.dll
  - Made wsinetd project dependant on msgdll (also added a dummy .lib for
	msgdll)
  - added a script to help in automatic building of releases.

2002/12/18 v0.3.6
  - Corrected wrong handling of command lines in the config file. Thanks Hua ;)

2001/11/20 v0.2.5
  - Enhanced service cleanup: on stop/shutdown, the service waits for all
    spawned threads/processes to terminate, (eventualy forcing their
    termination)
  - Maximum concurent connections handling: CHANGES IN THE CONFIG FILE FORMAT !
  - Removed debug output from the cleanup code (caused deadlocks upon cleanup)
  - Changes in the socket-to-pipe code (removed a dummy pseudo-optimization)
  - Enhanced the config file parser: paths with spaces are now allowed
  - Developpement status is still pre-alpha (deadlocks with MS Outlook 2K/imapd
    still unsolved).

2001/11/13 v0.1.4
	initial release

SourceForge Logo