Back to index

lightning-sunbird  0.9+nobinonly
client.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /****************************************************************************
00038  *  SSL client program that sets up a connection to SSL server, transmits   *
00039  *  some data and then reads the reply                                      *
00040  ****************************************************************************/ 
00041 
00042 #include <stdio.h>
00043 #include <string.h>
00044 
00045 #if defined(XP_UNIX)
00046 #include <unistd.h>
00047 #endif
00048 
00049 #include "prerror.h"
00050 
00051 #include "pk11func.h"
00052 #include "secitem.h"
00053 
00054 
00055 #include <stdlib.h>
00056 #include <errno.h>
00057 #include <fcntl.h>
00058 #include <stdarg.h>
00059 
00060 #include "nspr.h"
00061 #include "plgetopt.h"
00062 #include "prio.h"
00063 #include "prnetdb.h"
00064 #include "nss.h"
00065 
00066 #include "sslsample.h"
00067 
00068 #define RD_BUF_SIZE (60 * 1024)
00069 
00070 extern int ssl2CipherSuites[];
00071 extern int ssl3CipherSuites[];
00072 
00073 GlobalThreadMgr threadMGR;
00074 char *certNickname = NULL;
00075 char *hostName = NULL;
00076 char *password = NULL;
00077 unsigned short port = 0;
00078 
00079 static void
00080 Usage(const char *progName)
00081 {
00082        fprintf(stderr, 
00083          "Usage: %s [-n rsa_nickname] [-p port] [-d dbdir] [-c connections]\n"
00084          "          [-w dbpasswd] [-C cipher(s)] hostname\n",
00085        progName);
00086        exit(1);
00087 }
00088 
00089 PRFileDesc *
00090 setupSSLSocket(PRNetAddr *addr)
00091 {
00092        PRFileDesc         *tcpSocket;
00093        PRFileDesc         *sslSocket;
00094        PRSocketOptionData   socketOption;
00095        PRStatus            prStatus;
00096        SECStatus           secStatus;
00097 
00098 #if 0
00099 retry:
00100 #endif
00101 
00102        tcpSocket = PR_NewTCPSocket();
00103        if (tcpSocket == NULL) {
00104               errWarn("PR_NewTCPSocket");
00105        }
00106 
00107        /* Make the socket blocking. */
00108        socketOption.option              = PR_SockOpt_Nonblocking;
00109        socketOption.value.non_blocking = PR_FALSE;
00110 
00111        prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
00112        if (prStatus != PR_SUCCESS) {
00113               errWarn("PR_SetSocketOption");
00114               goto loser;
00115        } 
00116 
00117 #if 0
00118        /* Verify that a connection can be made to the socket. */
00119        prStatus = PR_Connect(tcpSocket, addr, PR_INTERVAL_NO_TIMEOUT);
00120        if (prStatus != PR_SUCCESS) {
00121               PRErrorCode err = PR_GetError();
00122               if (err == PR_CONNECT_REFUSED_ERROR) {
00123                      PR_Close(tcpSocket);
00124                      PR_Sleep(PR_MillisecondsToInterval(10));
00125                      fprintf(stderr, "Connection to port refused, retrying.\n");
00126                      goto retry;
00127               }
00128               errWarn("PR_Connect");
00129               goto loser;
00130        }
00131 #endif
00132 
00133        /* Import the socket into the SSL layer. */
00134        sslSocket = SSL_ImportFD(NULL, tcpSocket);
00135        if (!sslSocket) {
00136               errWarn("SSL_ImportFD");
00137               goto loser;
00138        }
00139 
00140        /* Set configuration options. */
00141        secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
00142        if (secStatus != SECSuccess) {
00143               errWarn("SSL_OptionSet:SSL_SECURITY");
00144               goto loser;
00145        }
00146 
00147        secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
00148        if (secStatus != SECSuccess) {
00149               errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
00150               goto loser;
00151        }
00152 
00153        /* Set SSL callback routines. */
00154        secStatus = SSL_GetClientAuthDataHook(sslSocket,
00155                                  (SSLGetClientAuthData)myGetClientAuthData,
00156                                  (void *)certNickname);
00157        if (secStatus != SECSuccess) {
00158               errWarn("SSL_GetClientAuthDataHook");
00159               goto loser;
00160        }
00161 
00162        secStatus = SSL_AuthCertificateHook(sslSocket,
00163                                           (SSLAuthCertificate)myAuthCertificate,
00164                                        (void *)CERT_GetDefaultCertDB());
00165        if (secStatus != SECSuccess) {
00166               errWarn("SSL_AuthCertificateHook");
00167               goto loser;
00168        }
00169 
00170        secStatus = SSL_BadCertHook(sslSocket, 
00171                                   (SSLBadCertHandler)myBadCertHandler, NULL);
00172        if (secStatus != SECSuccess) {
00173               errWarn("SSL_BadCertHook");
00174               goto loser;
00175        }
00176 
00177        secStatus = SSL_HandshakeCallback(sslSocket, 
00178                                         (SSLHandshakeCallback)myHandshakeCallback,
00179                                         NULL);
00180        if (secStatus != SECSuccess) {
00181               errWarn("SSL_HandshakeCallback");
00182               goto loser;
00183        }
00184 
00185        return sslSocket;
00186 
00187 loser:
00188 
00189        PR_Close(tcpSocket);
00190        return NULL;
00191 }
00192 
00193 
00194 const char requestString[] = {"GET /testfile HTTP/1.0\r\n\r\n" };
00195 
00196 SECStatus
00197 handle_connection(PRFileDesc *sslSocket, int connection)
00198 {
00199        int         countRead = 0;
00200        PRInt32  numBytes;
00201        char    *readBuffer;
00202 
00203        readBuffer = PORT_Alloc(RD_BUF_SIZE);
00204        if (!readBuffer) {
00205               exitErr("PORT_Alloc");
00206        }
00207 
00208        /* compose the http request here. */
00209 
00210        numBytes = PR_Write(sslSocket, requestString, strlen(requestString));
00211        if (numBytes <= 0) {
00212               errWarn("PR_Write");
00213               PR_Free(readBuffer);
00214               readBuffer = NULL;
00215               return SECFailure;
00216        }
00217 
00218        /* read until EOF */
00219        while (PR_TRUE) {
00220               numBytes = PR_Read(sslSocket, readBuffer, RD_BUF_SIZE);
00221               if (numBytes == 0) {
00222                      break; /* EOF */
00223               }
00224               if (numBytes < 0) {
00225                      errWarn("PR_Read");
00226                      break;
00227               }
00228               countRead += numBytes;
00229               fprintf(stderr, "***** Connection %d read %d bytes (%d total).\n", 
00230                      connection, numBytes, countRead );
00231               readBuffer[numBytes] = '\0';
00232               fprintf(stderr, "************\n%s\n************\n", readBuffer);
00233        }
00234 
00235        printSecurityInfo(sslSocket);
00236        
00237        PR_Free(readBuffer);
00238        readBuffer = NULL;
00239 
00240        /* Caller closes the socket. */
00241 
00242        fprintf(stderr, 
00243                "***** Connection %d read %d bytes total.\n", 
00244                connection, countRead);
00245 
00246        return SECSuccess;   /* success */
00247 }
00248 
00249 /* one copy of this function is launched in a separate thread for each
00250 ** connection to be made.
00251 */
00252 SECStatus
00253 do_connects(void *a, int connection)
00254 {
00255        PRNetAddr  *addr = (PRNetAddr *)a;
00256        PRFileDesc *sslSocket;
00257        PRHostEnt   hostEntry;
00258        char        buffer[PR_NETDB_BUF_SIZE];
00259        PRStatus    prStatus;
00260        PRIntn      hostenum;
00261        SECStatus   secStatus;
00262 
00263        /* Set up SSL secure socket. */
00264        sslSocket = setupSSLSocket(addr);
00265        if (sslSocket == NULL) {
00266               errWarn("setupSSLSocket");
00267               return SECFailure;
00268        }
00269 
00270        secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
00271        if (secStatus != SECSuccess) {
00272               errWarn("SSL_SetPKCS11PinArg");
00273               return secStatus;
00274        }
00275 
00276        secStatus = SSL_SetURL(sslSocket, hostName);
00277        if (secStatus != SECSuccess) {
00278               errWarn("SSL_SetURL");
00279               return secStatus;
00280        }
00281 
00282        /* Prepare and setup network connection. */
00283        prStatus = PR_GetHostByName(hostName, buffer, sizeof(buffer), &hostEntry);
00284        if (prStatus != PR_SUCCESS) {
00285               errWarn("PR_GetHostByName");
00286               return SECFailure;
00287        }
00288 
00289        hostenum = PR_EnumerateHostEnt(0, &hostEntry, port, addr);
00290        if (hostenum == -1) {
00291               errWarn("PR_EnumerateHostEnt");
00292               return SECFailure;
00293        }
00294 
00295        prStatus = PR_Connect(sslSocket, addr, PR_INTERVAL_NO_TIMEOUT);
00296        if (prStatus != PR_SUCCESS) {
00297               errWarn("PR_Connect");
00298               return SECFailure;
00299        }
00300 
00301        /* Established SSL connection, ready to send data. */
00302 #if 0
00303        secStatus = SSL_ForceHandshake(sslSocket);
00304        if (secStatus != SECSuccess) {
00305               errWarn("SSL_ForceHandshake");
00306               return secStatus;
00307        }
00308 #endif
00309 
00310        secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
00311        if (secStatus != SECSuccess) {
00312               errWarn("SSL_ResetHandshake");
00313               prStatus = PR_Close(sslSocket);
00314               if (prStatus != PR_SUCCESS) {
00315                      errWarn("PR_Close");
00316               }
00317               return secStatus;
00318        }
00319 
00320        secStatus = handle_connection(sslSocket, connection);
00321        if (secStatus != SECSuccess) {
00322               errWarn("handle_connection");
00323               return secStatus;
00324        }
00325 
00326        PR_Close(sslSocket);
00327        return SECSuccess;
00328 }
00329 
00330 void
00331 client_main(unsigned short      port, 
00332             int                      connections, 
00333             const char *        hostName)
00334 {
00335        int                  i;
00336        SECStatus     secStatus;
00337        PRStatus    prStatus;
00338        PRInt32     rv;
00339        PRNetAddr     addr;
00340        PRHostEnt   hostEntry;
00341        char        buffer[256];
00342 
00343        /* Setup network connection. */
00344        prStatus = PR_GetHostByName(hostName, buffer, 256, &hostEntry);
00345        if (prStatus != PR_SUCCESS) {
00346               exitErr("PR_GetHostByName");
00347        }
00348 
00349        rv = PR_EnumerateHostEnt(0, &hostEntry, port, &addr);
00350        if (rv < 0) {
00351               exitErr("PR_EnumerateHostEnt");
00352        }
00353 
00354        secStatus = launch_thread(&threadMGR, do_connects, &addr, 1);
00355        if (secStatus != SECSuccess) {
00356               exitErr("launch_thread");
00357        }
00358 
00359        if (connections > 1) {
00360               /* wait for the first connection to terminate, then launch the rest. */
00361               reap_threads(&threadMGR);
00362               /* Start up the connections */
00363               for (i = 2; i <= connections; ++i) {
00364                      secStatus = launch_thread(&threadMGR, do_connects, &addr, i);
00365                      if (secStatus != SECSuccess) {
00366                             errWarn("launch_thread");
00367                      }
00368               }
00369        }
00370 
00371        reap_threads(&threadMGR);
00372        destroy_thread_data(&threadMGR);
00373 }
00374 
00375 int
00376 main(int argc, char **argv)
00377 {
00378        char *               certDir      = ".";
00379        char *               progName     = NULL;
00380        int                                 connections  = 1;
00381        char *               cipherString = NULL;
00382        SECStatus            secStatus;
00383        PLOptState *         optstate;
00384        PLOptStatus          status;
00385 
00386        /* Call the NSPR initialization routines */
00387        PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
00388 
00389        progName = PL_strdup(argv[0]);
00390 
00391        hostName = NULL;
00392        optstate = PL_CreateOptState(argc, argv, "C:c:d:n:p:w:");
00393        while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
00394               switch(optstate->option) {
00395               case 'C' : cipherString = PL_strdup(optstate->value); break;
00396               case 'c' : connections = PORT_Atoi(optstate->value);  break;
00397               case 'd' : certDir = PL_strdup(optstate->value);      break;
00398               case 'n' : certNickname = PL_strdup(optstate->value); break;
00399               case 'p' : port = PORT_Atoi(optstate->value);         break;
00400               case 'w' : password = PL_strdup(optstate->value);     break;
00401               case '\0': hostName = PL_strdup(optstate->value);     break;
00402               default  : Usage(progName);
00403               }
00404        }
00405 
00406        if (port == 0 || hostName == NULL)
00407               Usage(progName);
00408 
00409        if (certDir == NULL) {
00410               certDir = PR_smprintf("%s/.netscape", getenv("HOME"));
00411        }
00412 
00413        /* Set our password function callback. */
00414        PK11_SetPasswordFunc(myPasswd);
00415 
00416        /* Initialize the NSS libraries. */
00417        secStatus = NSS_Init(certDir);
00418        if (secStatus != SECSuccess) {
00419               exitErr("NSS_Init");
00420        }
00421 
00422        /* All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy. */
00423        NSS_SetDomesticPolicy();
00424        SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
00425 
00426        /* all the SSL2 and SSL3 cipher suites are enabled by default. */
00427        if (cipherString) {
00428            int ndx;
00429 
00430            /* disable all the ciphers, then enable the ones we want. */
00431            disableAllSSLCiphers();
00432 
00433            while (0 != (ndx = *cipherString++)) {
00434               int *cptr;
00435               int  cipher;
00436 
00437               if (! isalpha(ndx))
00438                   Usage(progName);
00439               cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites;
00440               for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; )
00441                   /* do nothing */;
00442               if (cipher) {
00443                   SSL_CipherPrefSetDefault(cipher, PR_TRUE);
00444               }
00445            }
00446        }
00447 
00448        client_main(port, connections, hostName);
00449 
00450        if (NSS_Shutdown() != SECSuccess) {
00451             exit(1);
00452         }
00453        PR_Cleanup();
00454        return 0;
00455 }
00456