2 SSL client program that sets up a connection to a SSL server, transmits
3 the given input file and then writes the reply to the given output file.
5 Copyright (C) 2008, 2009 Red Hat Inc.
7 This file is part of systemtap, and is free software. You can
8 redistribute it and/or modify it under the terms of the GNU General Public
9 License as published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #define READ_BUFFER_SIZE (60 * 1024)
33 static char *hostName
= NULL
;
34 static unsigned short port
= 0;
35 static const char *infileName
= NULL
;
36 static const char *outfileName
= NULL
;
39 Usage(const char *progName
)
41 fprintf(stderr
, "Usage: %s -h hostname -p port -d dbdir -i infile -o outfile\n",
47 errWarn(char *function
)
49 PRErrorCode errorNumber
;
50 PRInt32 errorTextLength
;
54 errorNumber
= PR_GetError();
55 fprintf(stderr
, "Error in function %s: %d: ", function
, errorNumber
);
57 /* See if PR_GetErrorText can tell us what the error is. */
58 if (errorNumber
>= PR_NSPR_ERROR_BASE
&& errorNumber
<= PR_MAX_ERROR
)
60 errorTextLength
= PR_GetErrorTextLength ();
61 if (errorTextLength
!= 0) {
62 errorText
= PORT_Alloc(errorTextLength
);
63 rc
= PR_GetErrorText (errorText
);
65 fprintf (stderr
, "%s\n", errorText
);
72 /* Otherwise handle common errors ourselves. */
75 case SEC_ERROR_CA_CERT_INVALID
:
76 fputs ("The issuer's certificate is invalid\n", stderr
);
78 case SEC_ERROR_BAD_DATABASE
:
79 fputs ("The specified certificate database does not exist or is not valid\n", stderr
);
81 case SSL_ERROR_BAD_CERT_DOMAIN
:
82 fputs ("The requested domain name does not match the server's certificate\n", stderr
);
84 case PR_CONNECT_RESET_ERROR
:
85 fputs ("Connection reset by peer\n", stderr
);
88 fputs ("Unknown error\n", stderr
);
94 exitErr(char *function
)
97 /* Exit gracefully. */
98 /* ignoring return value of NSS_Shutdown as code exits with 1*/
99 (void) NSS_Shutdown();
107 PRFileDesc
*tcpSocket
;
108 PRFileDesc
*sslSocket
;
109 PRSocketOptionData socketOption
;
113 tcpSocket
= PR_NewTCPSocket();
114 if (tcpSocket
== NULL
)
116 errWarn("PR_NewTCPSocket");
119 /* Make the socket blocking. */
120 socketOption
.option
= PR_SockOpt_Nonblocking
;
121 socketOption
.value
.non_blocking
= PR_FALSE
;
123 prStatus
= PR_SetSocketOption(tcpSocket
, &socketOption
);
124 if (prStatus
!= PR_SUCCESS
)
126 errWarn("PR_SetSocketOption");
130 /* Import the socket into the SSL layer. */
131 sslSocket
= SSL_ImportFD(NULL
, tcpSocket
);
134 errWarn("SSL_ImportFD");
138 /* Set configuration options. */
139 secStatus
= SSL_OptionSet(sslSocket
, SSL_SECURITY
, PR_TRUE
);
140 if (secStatus
!= SECSuccess
)
142 errWarn("SSL_OptionSet:SSL_SECURITY");
146 secStatus
= SSL_OptionSet(sslSocket
, SSL_HANDSHAKE_AS_CLIENT
, PR_TRUE
);
147 if (secStatus
!= SECSuccess
)
149 errWarn("SSL_OptionSet:SSL_HANDSHAKE_AS_CLIENT");
153 /* Set SSL callback routines. */
154 #if 0 /* no client authentication */
155 secStatus
= SSL_GetClientAuthDataHook(sslSocket
,
156 (SSLGetClientAuthData
)myGetClientAuthData
,
157 (void *)certNickname
);
158 if (secStatus
!= SECSuccess
)
160 errWarn("SSL_GetClientAuthDataHook");
164 #if 0 /* Use the default */
165 secStatus
= SSL_AuthCertificateHook(sslSocket
,
166 (SSLAuthCertificate
)myAuthCertificate
,
167 (void *)CERT_GetDefaultCertDB());
168 if (secStatus
!= SECSuccess
)
170 errWarn("SSL_AuthCertificateHook");
174 #if 0 /* Use the default */
175 secStatus
= SSL_BadCertHook(sslSocket
,
176 (SSLBadCertHandler
)myBadCertHandler
, NULL
);
177 if (secStatus
!= SECSuccess
)
179 errWarn("SSL_BadCertHook");
183 #if 0 /* No handshake callback */
184 secStatus
= SSL_HandshakeCallback(sslSocket
, myHandshakeCallback
, NULL
);
185 if (secStatus
!= SECSuccess
)
187 errWarn("SSL_HandshakeCallback");
201 handle_connection(PRFileDesc
*sslSocket
)
209 PRFileDesc
*local_file_fd
;
212 /* read and send the data. */
213 /* Try to open the local file named.
214 * If successful, then write it to the server
216 prStatus
= PR_GetFileInfo(infileName
, &info
);
217 if (prStatus
!= PR_SUCCESS
||
218 info
.type
!= PR_FILE_FILE
||
221 fprintf (stderr
, "could not find input file %s\n", infileName
);
225 local_file_fd
= PR_Open(infileName
, PR_RDONLY
, 0);
226 if (local_file_fd
== NULL
)
228 fprintf (stderr
, "could not open input file %s\n", infileName
);
232 /* Send the file size first, so the server knows when it has the entire file. */
233 numBytes
= PR_Write(sslSocket
, & info
.size
, sizeof (info
.size
));
240 /* Transmit the local file across the socket. */
241 numBytes
= PR_TransmitFile(sslSocket
, local_file_fd
,
243 PR_TRANSMITFILE_KEEP_OPEN
,
244 PR_INTERVAL_NO_TIMEOUT
);
247 errWarn("PR_TransmitFile");
252 /* Transmitted bytes successfully. */
253 fprintf(stderr
, "PR_TransmitFile wrote %d bytes from %s\n",
254 numBytes
, infileName
);
257 PR_Close(local_file_fd
);
260 readBuffer
= PORT_Alloc(READ_BUFFER_SIZE
);
262 exitErr("PORT_Alloc");
264 local_file_fd
= PR_Open(outfileName
, PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
,
265 PR_IRUSR
| PR_IWUSR
| PR_IRGRP
| PR_IWGRP
| PR_IROTH
);
266 if (local_file_fd
== NULL
)
268 fprintf (stderr
, "could not open output file %s\n", outfileName
);
273 numBytes
= PR_Read(sslSocket
, readBuffer
, READ_BUFFER_SIZE
);
283 countRead
+= numBytes
;
285 /* Write to output file */
286 numBytes
= PR_Write(local_file_fd
, readBuffer
, numBytes
);
289 fprintf (stderr
, "could not write to %s\n", outfileName
);
293 fprintf(stderr
, "***** Connection read %d bytes (%d total).\n",
294 numBytes
, countRead
);
295 readBuffer
[numBytes
] = '\0';
296 fprintf(stderr
, "************\n%s\n************\n", readBuffer
);
301 PR_Close(local_file_fd
);
303 /* Caller closes the socket. */
305 fprintf(stderr
, "***** Connection read %d bytes total.\n", countRead
);
311 /* make the connection.
314 do_connect(PRNetAddr
*addr
)
316 PRFileDesc
*sslSocket
;
320 char buffer
[PR_NETDB_BUF_SIZE
];
325 secStatus
= SECSuccess
;
327 /* Set up SSL secure socket. */
328 sslSocket
= setupSSLSocket();
329 if (sslSocket
== NULL
)
331 errWarn("setupSSLSocket");
335 #if 0 /* no client authentication */
336 secStatus
= SSL_SetPKCS11PinArg(sslSocket
, password
);
337 if (secStatus
!= SECSuccess
)
339 errWarn("SSL_SetPKCS11PinArg");
344 secStatus
= SSL_SetURL(sslSocket
, hostName
);
345 if (secStatus
!= SECSuccess
)
347 errWarn("SSL_SetURL");
350 #if 0 /* Already done? */
351 /* Prepare and setup network connection. */
352 prStatus
= PR_GetHostByName(hostName
, buffer
, sizeof(buffer
), &hostEntry
);
353 if (prStatus
!= PR_SUCCESS
)
355 errWarn("PR_GetHostByName");
356 secStatus
= SECFailure
;
360 hostenum
= PR_EnumerateHostEnt(0, &hostEntry
, port
, addr
);
363 errWarn("PR_EnumerateHostEnt");
364 secStatus
= SECFailure
;
368 prStatus
= PR_Connect(sslSocket
, addr
, PR_INTERVAL_NO_TIMEOUT
);
369 if (prStatus
!= PR_SUCCESS
)
371 errWarn("PR_Connect");
372 secStatus
= SECFailure
;
376 /* Established SSL connection, ready to send data. */
377 secStatus
= SSL_ResetHandshake(sslSocket
, /* asServer */ PR_FALSE
);
378 if (secStatus
!= SECSuccess
)
380 errWarn("SSL_ResetHandshake");
384 /* This is normally done automatically on the first I/O operation,
385 but doing it here catches any authentication problems early. */
386 secStatus
= SSL_ForceHandshake(sslSocket
);
387 if (secStatus
!= SECSuccess
)
389 errWarn("SSL_ForceHandshake");
393 secStatus
= handle_connection(sslSocket
);
394 if (secStatus
!= SECSuccess
)
396 errWarn("handle_connection");
401 prStatus
= PR_Close(sslSocket
);
402 if (prStatus
!= PR_SUCCESS
)
409 client_main(unsigned short port
)
416 char buffer
[PR_NETDB_BUF_SIZE
];
418 /* Setup network connection. */
419 prStatus
= PR_GetHostByName(hostName
, buffer
, sizeof (buffer
), &hostEntry
);
420 if (prStatus
!= PR_SUCCESS
)
421 exitErr("PR_GetHostByName");
423 rv
= PR_EnumerateHostEnt(0, &hostEntry
, port
, &addr
);
425 exitErr("PR_EnumerateHostEnt");
427 secStatus
= do_connect (&addr
);
428 if (secStatus
!= SECSuccess
)
429 exitErr("do_connect");
432 #if 0 /* No client authorization */
434 myPasswd(PK11SlotInfo
*info
, PRBool retry
, void *arg
)
436 char * passwd
= NULL
;
438 if ( (!retry
) && arg
)
439 passwd
= PORT_Strdup((char *)arg
);
446 main(int argc
, char **argv
)
448 char * certDir
= NULL
;
449 char * progName
= NULL
;
451 PLOptState
*optstate
;
454 /* Call the NSPR initialization routines */
455 PR_Init( PR_SYSTEM_THREAD
, PR_PRIORITY_NORMAL
, 1);
457 progName
= PL_strdup(argv
[0]);
460 optstate
= PL_CreateOptState(argc
, argv
, "d:h:i:o:p:");
461 while ((status
= PL_GetNextOpt(optstate
)) == PL_OPT_OK
)
463 switch(optstate
->option
)
465 case 'd' : certDir
= PL_strdup(optstate
->value
); break;
466 case 'h' : hostName
= PL_strdup(optstate
->value
); break;
467 case 'i' : infileName
= PL_strdup(optstate
->value
); break;
468 case 'o' : outfileName
= PL_strdup(optstate
->value
); break;
469 case 'p' : port
= PORT_Atoi(optstate
->value
); break;
471 default : Usage(progName
);
475 if (port
== 0 || hostName
== NULL
|| infileName
== NULL
|| outfileName
== NULL
|| certDir
== NULL
)
478 #if 0 /* no client authentication */
479 /* Set our password function callback. */
480 PK11_SetPasswordFunc(myPasswd
);
483 /* Initialize the NSS libraries. */
484 secStatus
= NSS_Init(certDir
);
485 if (secStatus
!= SECSuccess
)
488 /* All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy. */
489 NSS_SetDomesticPolicy();
499 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */