Patrick,
Patrick wrote:
> About forking and NSS:
I have to stop your right here :
NSS is much better suited for multithreaded applications than
multiprocess applications. Multiprocess applications are supported, as
well as hybrid multiprocess / multithreaded applications, but they are a
lot more work to implement.
An NSS multiprocess server requires a very particular architecture. NSS
should never be initialized in a process before a fork() call due to the
undefined behavior of a PKCS#11 module after a fork. In general NSS
The softoken which handles the cert and key databases is a PKCS#11
module, and it is not safe for forking, even if NSS is initialized for
read-only operation. This is because when the softoken is initialized,
it opens database files. In Unix, there is a single underlying file
descriptor context for that handle when you fork. So if you do, and your
child processes all do I/O do the database at the same time, such as to
look for a certificate or a CRL, they will do read() and seek() on the
same fd, causing unpredictable results. The only way to solve the
problem is to have each child process open separate files, read-only,
and this can only be done by delaying the NSS_Initialize call until
after the fork() call.
Hardware PKCS#11 modules are even worse WRT fork() safety, and will
almost never work correctly after a fork. The reason is very similar to
why softoken won't work - the PKCS#11 vendor library open a file handle
to their physical device driver, and then the file handle gets shared
between the child processes, causing a mess.
For NSS to work with forking, the parent process must call
SSL_ConfigMPServerSIDCache once . It is then free to fork().
Child processes must then each call SSL_InheritMPServerSIDCache() , then
NSS_Initialize().
Typically, the parent process will creates the listen socket, and
initialize the multiprocess SSL server cache . It will then fork a
number of child server processes.
Each child will then initialize NSS and accept connections from the
listen socket. You have several choices there.
1) convert the listen socket to SSL, using SSL_ImportFD and the other
SSL calls. This is one-time overhead
2) convert accepted sockets to SSL . This will be n-time overhead, and
will only work well if you use connection, I highly recommend that you
use a model socket to do so, otherwise it will be very slow because you
have to keep making a bunch of other setup calls on each accepted socket.
> How best does one use NSS to do create an app that accepts connection
> requests and forking off children to handle them? That is, how best to do
> the following but with NSS:
>
> /*
> * Loop forever accepting connection requests and forking off
> * children to handle them.
> */
> while (1) {
> addrlen = sizeof(his_addr);
> fd2 = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
> if (fork() == 0) {
> /* child */
> (void) dup2(fd2, 0);
> (void) dup2(fd2, 1);
> close(ctl_sock);
> break;
> }
> close(fd2);
> }
>
> I don't see the equivalent to dup2 in NSS, no way to duplicate an NSPR file
> descriptor... Another solution could be to import the new fd2 into NSPR/NSS:
> I thought that at one time NSS had a function for import an exiting FD into
> NSPR. Is it gone?
There is an NSPR function to convert a native fd to a PRFileDesc, called
PR_FileDesc2NativeHandle , in private/pprio.h . Since it is in a
private header file, this means that API is subject to change, but for
now it can be used.
As far as your program above, to get it working from a strictly
functional point of view, all you need to do is add a call to
SSL_ConfigMPServerSIDCache before your while loop, and in your "child"
block of code, add calls to SSL_ConfigInheritMPServerSIDCache ,
NSS_Initialize, PR_FileDesc2NativeHandle, and SSL_ImportFD . Actually,
there are a few more NSS calls needed after SSL_ImportFD, but you get
the idea.
You will find that this will work, but it will perform extremely poorly
because NSS will be reinitialized each time a connection comes in. This
is the wrong program architecture for NSS. You need to prefork the
number of processes you need, so they can each initialize NSS and incur
the setup overhead first, and then have them accept connections, either
in a loop with PR_Accept, or better yet, by making each of your
processes multithreaded. The later is a hybrid multiprocess /
multithreaded architecture, and gives the best performance, but it is
also the hardest to implement. I was part of a team of Sun and Netscape
engineers that spent years optimizing the code for a multiprocess
multithreaded SSL server using NSS. The result of our efforts are in the
latest 6.x versions of iPlanet Web Server / Netscape Enterprise Server.
--
"Except for the lack of debugging and the ps thing, [Linux] kernel
threads are generally fine right now. And if you're not too fussed
about the more fiddly details of POSIX threads, and your application
doesn't spend most of its time in thread creation, then LinuxThreads
is great too."
Linux-Kernel archive