static OSStatus sslServe( otSocket listenSock, SSLProtocol tryVersion, const char *hostName, // e.g., "www.amazon.com" CFArrayRef serverCerts, // required CFArrayRef encryptServerCerts, // optional CSSM_BOOL allowExpired, CSSM_BOOL allowAnyRoot, char cipherRestrict, // '2', 'd'. etc...'\0' for no // restriction SSLAuthenticate authenticate, CSSM_BOOL resumableEnable, CSSM_BOOL silent, // no stdout CSSM_BOOL pause, SSLProtocol *negVersion, // RETURNED SSLCipherSuite *negCipher, // RETURNED CFArrayRef *peerCerts) // mallocd & RETURNED { otSocket acceptSock; PeerSpec peerId; OSStatus ortn; SSLContextRef ctx = NULL; UInt32 length; uint8 rcvBuf[RCV_BUF_SIZE]; char *outMsg = SERVER_MESSAGE; *negVersion = kSSLProtocolUnknown; *negCipher = SSL_NULL_WITH_NULL_NULL; *peerCerts = NULL; #if IGNORE_SIGPIPE signal(SIGPIPE, sigpipe); #endif /* first wait for a connection */ if(!silent) { printf("Waiting for client connection..."); fflush(stdout); } ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId); if(ortn) { printf("AcceptClientConnection returned %d; aborting\n", ortn); return ortn; } /* * Set up a SecureTransport session. * First the standard calls. */ ortn = SSLNewContext(true, &ctx); if(ortn) { printSslErrStr("SSLNewContext", ortn); goto cleanup; } ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); if(ortn) { printSslErrStr("SSLSetIOFuncs", ortn); goto cleanup; } ortn = SSLSetProtocolVersion(ctx, tryVersion); if(ortn) { printSslErrStr("SSLSetProtocolVersion", ortn); goto cleanup; } ortn = SSLSetConnection(ctx, acceptSock); if(ortn) { printSslErrStr("SSLSetConnection", ortn); goto cleanup; } ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1); if(ortn) { printSslErrStr("SSLSetPeerDomainName", ortn); goto cleanup; } /* have to do these options befor setting server certs */ if(allowExpired) { ortn = SSLSetAllowsExpiredCerts(ctx, true); if(ortn) { printSslErrStr("SSLSetAllowExpiredCerts", ortn); goto cleanup; } } if(allowAnyRoot) { ortn = SSLSetAllowsAnyRoot(ctx, true); if(ortn) { printSslErrStr("SSLSetAllowAnyRoot", ortn); goto cleanup; } } ortn = SSLSetCertificate(ctx, serverCerts); if(ortn) { printSslErrStr("SSLSetCertificate", ortn); goto cleanup; } if(encryptServerCerts) { ortn = SSLSetEncryptionCertificate(ctx, encryptServerCerts); if(ortn) { printSslErrStr("SSLSetEncryptionCertificate", ortn); goto cleanup; } } /* * SecureTransport options. */ if(resumableEnable) { ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); if(ortn) { printSslErrStr("SSLSetPeerID", ortn); goto cleanup; } } if(cipherRestrict != '\0') { ortn = setCipherRestrictions(ctx, cipherRestrict); if(ortn) { goto cleanup; } } #if AUTHENTICATE_ENABLE if(authenticate != kNeverAuthenticate) { ortn = SSLSetClientSideAuthenticate(ctx, authenticate); if(ortn) { printSslErrStr("SSLSetClientSideAuthenticate", ortn); goto cleanup; } } #endif if(pause) { doPause("SSLContext initialized"); } /* Perform SSL/TLS handshake */ do { ortn = SSLHandshake(ctx); if((ortn == errSSLWouldBlock) && !silent) { /* keep UI responsive */ outputDot(); } } while (ortn == errSSLWouldBlock); /* this works even if handshake failed due to cert chain invalid */ copyPeerCerts(ctx, peerCerts); SSLGetNegotiatedCipher(ctx, negCipher); SSLGetNegotiatedProtocolVersion(ctx, negVersion); if(!silent) { printf("\n"); } if(ortn) { goto cleanup; } if(pause) { doPause("SSLContext handshake complete"); } /* wait for one complete line or user says they've had enough */ while(ortn == noErr) { length = sizeof(rcvBuf); ortn = SSLRead(ctx, rcvBuf, length, &length); if(length == 0) { /* keep UI responsive */ outputDot(); } else { /* print what we have */ printf("client request: "); dumpAscii(rcvBuf, length); } if(pause) { /* allow user to bail */ char resp; fpurge(stdin); printf("\nMore client request (y/anything): "); resp = getchar(); if(resp != 'y') { break; } } /* poor person's line completion scan */ for(unsigned i=0; i<length; i++) { if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) { /* a labelled break would be nice here.... */ goto serverResp; } } if (ortn == errSSLWouldBlock) { ortn = noErr; } } serverResp: if(pause) { doPause("Client GET msg received"); } /* send out canned response */ length = strlen(outMsg); ortn = SSLWrite(ctx, outMsg, length, &length); if(ortn) { printSslErrStr("SSLWrite", ortn); } if(pause) { doPause("Server response sent"); } if (ortn == noErr) { ortn = SSLClose(ctx); } cleanup: if(acceptSock) { endpointShutdown(acceptSock); } if(ctx) { SSLDisposeContext(ctx); } /* FIXME - dispose of serverCerts */ return ortn; }
/* * params->lock is held for us by runSession() - we use it as a semapahore by * unlocking it when we've created a port to listen on. * This is generally run from a thread via sslRunSession() and * sslServerThread() in sslAppUtils.cpp. */ OSStatus sslAppServe( SslAppTestParams *params) { otSocket listenSock = 0; otSocket acceptSock = 0; PeerSpec peerId; OSStatus ortn; SSLContextRef ctx = NULL; SecKeychainRef serverKc = nil; CFArrayRef serverCerts = nil; sslThrDebug("Server", "starting"); params->negVersion = kSSLProtocolUnknown; params->negCipher = SSL_NULL_WITH_NULL_NULL; params->ortn = noHardwareErr; /* set up a socket on which to listen */ for(unsigned retry=0; retry<BIND_RETRIES; retry++) { ortn = ListenForClients(params->port, params->nonBlocking, &listenSock); switch(ortn) { case errSecSuccess: break; case errSecOpWr: /* port already in use - try another */ params->port++; if(params->verbose || THREADING_DEBUG) { printf("...retrying ListenForClients at port %d\n", params->port); } break; default: break; } if(ortn != errSecOpWr) { break; } } /* let main thread know a socket is ready */ if(pthread_mutex_lock(¶ms->pthreadMutex)) { printf("***Error acquiring server lock; aborting.\n"); return -1; } params->serverReady = true; if(pthread_cond_broadcast(¶ms->pthreadCond)) { printf("***Error waking main thread; aborting.\n"); return -1; } if(pthread_mutex_unlock(¶ms->pthreadMutex)) { printf("***Error acquiring server lock; aborting.\n"); return -1; } if(ortn) { printf("ListenForClients returned %d; aborting\n", (int)ortn); return ortn; } /* wait for a connection */ if(params->verbose) { printf("Waiting for client connection..."); fflush(stdout); } ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId); if(ortn) { printf("AcceptClientConnection returned %d; aborting\n", (int)ortn); return ortn; } /* * Set up a SecureTransport session. */ ortn = SSLNewContext(true, &ctx); if(ortn) { printSslErrStr("SSLNewContext", ortn); goto cleanup; } ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); if(ortn) { printSslErrStr("SSLSetIOFuncs", ortn); goto cleanup; } ortn = SSLSetConnection(ctx, (SSLConnectionRef)acceptSock); if(ortn) { printSslErrStr("SSLSetConnection", ortn); goto cleanup; } if(params->anchorFile) { ortn = sslAddTrustedRoot(ctx, params->anchorFile, params->replaceAnchors); if(ortn) { goto cleanup; } } if(params->myCertKcName != NULL) { /* if not, better be trying anonymous diff-hellman... :-) */ serverCerts = getSslCerts(params->myCertKcName, false, false, NULL, &serverKc); if(serverCerts == nil) { exit(1); } if(params->password) { ortn = SecKeychainUnlock(serverKc, strlen(params->password), (void *)params->password, true); if(ortn) { printf("SecKeychainUnlock returned %d\n", (int)ortn); /* oh well */ } } if(params->idIsTrustedRoot) { /* assume this is a root we want to implicitly trust */ ortn = addIdentityAsTrustedRoot(ctx, serverCerts); if(ortn) { goto cleanup; } } ortn = SSLSetCertificate(ctx, serverCerts); if(ortn) { printSslErrStr("SSLSetCertificate", ortn); goto cleanup; } } if(params->disableCertVerify) { ortn = SSLSetEnableCertVerify(ctx, false); if(ortn) { printSslErrStr("SSLSetEnableCertVerify", ortn); goto cleanup; } } ortn = sslSetProtocols(ctx, params->acceptedProts, params->tryVersion); if(ortn) { goto cleanup; } if(params->resumeEnable) { ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); if(ortn) { printSslErrStr("SSLSetPeerID", ortn); goto cleanup; } } if(params->ciphers != NULL) { ortn = sslSetEnabledCiphers(ctx, params->ciphers); if(ortn) { goto cleanup; } } if(params->authenticate != kNeverAuthenticate) { ortn = SSLSetClientSideAuthenticate(ctx, params->authenticate); if(ortn) { printSslErrStr("SSLSetClientSideAuthenticate", ortn); goto cleanup; } } if(params->dhParams) { #if JAGUAR_BUILD printf("***Diffie-Hellman not supported in this config.\n"); #else ortn = SSLSetDiffieHellmanParams(ctx, params->dhParams, params->dhParamsLen); if(ortn) { printSslErrStr("SSLSetDiffieHellmanParams", ortn); goto cleanup; } #endif } /* Perform SSL/TLS handshake */ do { ortn = SSLHandshake(ctx); if((ortn == errSSLWouldBlock) && !params->silent) { /* keep UI responsive */ sslOutputDot(); } } while (ortn == errSSLWouldBlock); SSLGetClientCertificateState(ctx, ¶ms->certState); SSLGetNegotiatedCipher(ctx, ¶ms->negCipher); SSLGetNegotiatedProtocolVersion(ctx, ¶ms->negVersion); if(params->verbose) { printf("\n"); } if(ortn) { goto cleanup; } /* wait for one complete line */ char readBuf[READBUF_LEN]; size_t length; while(ortn == errSecSuccess) { length = READBUF_LEN; ortn = SSLRead(ctx, readBuf, length, &length); if (ortn == errSSLWouldBlock) { /* keep trying */ ortn = errSecSuccess; continue; } if(length == 0) { /* keep trying */ continue; } /* poor person's line completion scan */ for(unsigned i=0; i<length; i++) { if((readBuf[i] == '\n') || (readBuf[i] == '\r')) { goto serverResp; } } } serverResp: /* send out canned response */ ortn = SSLWrite(ctx, SERVER_MESSAGE, strlen(SERVER_MESSAGE), &length); if(ortn) { printSslErrStr("SSLWrite", ortn); } cleanup: /* * always do close, even on error - to flush outgoing write queue */ if(ctx) { OSStatus cerr = SSLClose(ctx); if(ortn == errSecSuccess) { ortn = cerr; } } if(acceptSock) { while(!params->clientDone && !params->serverAbort) { usleep(100); } endpointShutdown(acceptSock); } if(listenSock) { endpointShutdown(listenSock); } if(ctx) { SSLDisposeContext(ctx); } params->ortn = ortn; sslThrDebug("Server", "done"); return ortn; }
static OSStatus sslServe( otSocket listenSock, unsigned short portNum, SSLProtocol tryVersion, // only used if acceptedProts NULL const char *acceptedProts, CFArrayRef serverCerts, // required char *password, // optional CFArrayRef encryptServerCerts, // optional bool allowExpired, bool allowAnyRoot, bool allowExpiredRoot, bool disableCertVerify, char *anchorFile, bool replaceAnchors, char cipherRestrict, // '2', 'd'. etc...'\0' for no // restriction SSLAuthenticate authenticate, unsigned char *dhParams, // optional D-H parameters unsigned dhParamsLen, CFArrayRef acceptableDNList, // optional bool resumableEnable, uint32_t sessionCacheTimeout,// optional bool disableAnonCiphers, bool silent, // no stdout bool pause, SSLProtocol *negVersion, // RETURNED SSLCipherSuite *negCipher, // RETURNED SSLClientCertificateState *certState, // RETURNED Boolean *sessionWasResumed, // RETURNED unsigned char *sessionID, // mallocd by caller, RETURNED size_t *sessionIDLength, // RETURNED CFArrayRef *peerCerts, // mallocd & RETURNED char **argv) { otSocket acceptSock; PeerSpec peerId; OSStatus ortn; SSLContextRef ctx = NULL; size_t length; uint8_t rcvBuf[RCV_BUF_SIZE]; const char *outMsg = SERVER_MESSAGE; *negVersion = kSSLProtocolUnknown; *negCipher = SSL_NULL_WITH_NULL_NULL; *peerCerts = NULL; #if IGNORE_SIGPIPE signal(SIGPIPE, sigpipe); #endif /* first wait for a connection */ if(!silent) { printf("Waiting for client connection on port %u...", portNum); fflush(stdout); } ortn = AcceptClientConnection(listenSock, &acceptSock, &peerId); if(ortn) { printf("AcceptClientConnection returned %d; aborting\n", (int)ortn); return ortn; } /* * Set up a SecureTransport session. * First the standard calls. */ ortn = SSLNewContext(true, &ctx); if(ortn) { printSslErrStr("SSLNewContext", ortn); goto cleanup; } ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); if(ortn) { printSslErrStr("SSLSetIOFuncs", ortn); goto cleanup; } ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)acceptSock); if(ortn) { printSslErrStr("SSLSetConnection", ortn); goto cleanup; } /* have to do these options befor setting server certs */ if(allowExpired) { ortn = SSLSetAllowsExpiredCerts(ctx, true); if(ortn) { printSslErrStr("SSLSetAllowExpiredCerts", ortn); goto cleanup; } } if(allowAnyRoot) { ortn = SSLSetAllowsAnyRoot(ctx, true); if(ortn) { printSslErrStr("SSLSetAllowAnyRoot", ortn); goto cleanup; } } if(anchorFile) { ortn = sslAddTrustedRoot(ctx, anchorFile, replaceAnchors); if(ortn) { printf("***Error obtaining anchor file %s\n", anchorFile); goto cleanup; } } if(serverCerts != NULL) { if(anchorFile == NULL) { /* no specific anchors, so assume we want to trust this one */ ortn = addIdentityAsTrustedRoot(ctx, serverCerts); if(ortn) { goto cleanup; } } ortn = SSLSetCertificate(ctx, serverCerts); if(ortn) { printSslErrStr("SSLSetCertificate", ortn); goto cleanup; } } if(encryptServerCerts) { ortn = SSLSetEncryptionCertificate(ctx, encryptServerCerts); if(ortn) { printSslErrStr("SSLSetEncryptionCertificate", ortn); goto cleanup; } } if(allowExpiredRoot) { ortn = SSLSetAllowsExpiredRoots(ctx, true); if(ortn) { printSslErrStr("SSLSetAllowsExpiredRoots", ortn); goto cleanup; } } if(disableCertVerify) { ortn = SSLSetEnableCertVerify(ctx, false); if(ortn) { printSslErrStr("SSLSetEnableCertVerify", ortn); goto cleanup; } } /* * SecureTransport options. */ if(acceptedProts) { ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false); if(ortn) { printSslErrStr("SSLSetProtocolVersionEnabled(all off)", ortn); goto cleanup; } for(const char *cp = acceptedProts; *cp; cp++) { SSLProtocol prot = kSSLProtocolUnknown; switch(*cp) { case '2': prot = kSSLProtocol2; break; case '3': prot = kSSLProtocol3; break; case 't': prot = kTLSProtocol1; break; default: usage(argv); } ortn = SSLSetProtocolVersionEnabled(ctx, prot, true); if(ortn) { printSslErrStr("SSLSetProtocolVersionEnabled", ortn); goto cleanup; } } } else { ortn = SSLSetProtocolVersion(ctx, tryVersion); if(ortn) { printSslErrStr("SSLSetProtocolVersion", ortn); goto cleanup; } } if(resumableEnable) { ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); if(ortn) { printSslErrStr("SSLSetPeerID", ortn); goto cleanup; } } if(cipherRestrict != '\0') { ortn = sslSetCipherRestrictions(ctx, cipherRestrict); if(ortn) { goto cleanup; } } if(authenticate != kNeverAuthenticate) { ortn = SSLSetClientSideAuthenticate(ctx, authenticate); if(ortn) { printSslErrStr("SSLSetClientSideAuthenticate", ortn); goto cleanup; } } if(dhParams) { ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen); if(ortn) { printSslErrStr("SSLSetDiffieHellmanParams", ortn); goto cleanup; } } if(sessionCacheTimeout) { ortn = SSLSetSessionCacheTimeout(ctx, sessionCacheTimeout); if(ortn) { printSslErrStr("SSLSetSessionCacheTimeout", ortn); goto cleanup; } } if(disableAnonCiphers) { ortn = SSLSetAllowAnonymousCiphers(ctx, false); if(ortn) { printSslErrStr("SSLSetAllowAnonymousCiphers", ortn); goto cleanup; } /* quickie test of the getter */ Boolean e; ortn = SSLGetAllowAnonymousCiphers(ctx, &e); if(ortn) { printSslErrStr("SSLGetAllowAnonymousCiphers", ortn); goto cleanup; } if(e) { printf("***SSLGetAllowAnonymousCiphers() returned true; expected false\n"); ortn = errSecIO; goto cleanup; } } /* XXX/cs if(acceptableDNList) { ortn = SSLSetCertificateAuthorities(ctx, acceptableDNList, TRUE); if(ortn) { printSslErrStr("SSLSetCertificateAuthorities", ortn); goto cleanup; } } */ /* end options */ if(pause) { doPause("SSLContext initialized"); } /* Perform SSL/TLS handshake */ do { ortn = SSLHandshake(ctx); if((ortn == errSSLWouldBlock) && !silent) { /* keep UI responsive */ sslOutputDot(); } } while (ortn == errSSLWouldBlock); /* this works even if handshake failed due to cert chain invalid */ copyPeerCerts(ctx, peerCerts); SSLGetClientCertificateState(ctx, certState); SSLGetNegotiatedCipher(ctx, negCipher); SSLGetNegotiatedProtocolVersion(ctx, negVersion); *sessionIDLength = MAX_SESSION_ID_LENGTH; SSLGetResumableSessionInfo(ctx, sessionWasResumed, sessionID, sessionIDLength); if(!silent) { printf("\n"); } if(ortn) { goto cleanup; } if(pause) { doPause("SSLContext handshake complete"); } /* wait for one complete line or user says they've had enough */ while(ortn == errSecSuccess) { length = sizeof(rcvBuf); ortn = SSLRead(ctx, rcvBuf, length, &length); if(length == 0) { /* keep UI responsive */ sslOutputDot(); } else { /* print what we have */ printf("client request: "); dumpAscii(rcvBuf, length); } if(pause) { /* allow user to bail */ char resp; fpurge(stdin); printf("\nMore client request (y/anything): "); resp = getchar(); if(resp != 'y') { break; } } /* poor person's line completion scan */ for(unsigned i=0; i<length; i++) { if((rcvBuf[i] == '\n') || (rcvBuf[i] == '\r')) { /* a labelled break would be nice here.... */ goto serverResp; } } if (ortn == errSSLWouldBlock) { ortn = errSecSuccess; } } serverResp: if(pause) { doPause("Client GET msg received"); } /* send out canned response */ length = strlen(outMsg); ortn = SSLWrite(ctx, outMsg, length, &length); if(ortn) { printSslErrStr("SSLWrite", ortn); } if(pause) { doPause("Server response sent"); } cleanup: /* * always do close, even on error - to flush outgoing write queue */ OSStatus cerr = SSLClose(ctx); if(ortn == errSecSuccess) { ortn = cerr; } if(acceptSock) { endpointShutdown(acceptSock); } if(ctx) { SSLDisposeContext(ctx); } /* FIXME - dispose of serverCerts */ return ortn; }
/* relies on SSLSetProtocolVersionEnabled */ OSStatus sslAppClient( SslAppTestParams *params) { PeerSpec peerId; otSocket sock = 0; OSStatus ortn; SSLContextRef ctx = NULL; SecKeychainRef clientKc = nil; CFArrayRef clientCerts = nil; sslThrDebug("Client", "starting"); params->negVersion = kSSLProtocolUnknown; params->negCipher = SSL_NULL_WITH_NULL_NULL; params->ortn = noHardwareErr; /* first make sure requested server is there */ ortn = MakeServerConnection(params->hostName, params->port, params->nonBlocking, &sock, &peerId); if(ortn) { printf("MakeServerConnection returned %d; aborting\n", (int)ortn); return ortn; } /* * Set up a SecureTransport session. */ ortn = SSLNewContext(false, &ctx); if(ortn) { printSslErrStr("SSLNewContext", ortn); goto cleanup; } ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); if(ortn) { printSslErrStr("SSLSetIOFuncs", ortn); goto cleanup; } ortn = SSLSetConnection(ctx, (SSLConnectionRef)sock); if(ortn) { printSslErrStr("SSLSetConnection", ortn); goto cleanup; } if(!params->skipHostNameCheck) { ortn = SSLSetPeerDomainName(ctx, params->hostName, strlen(params->hostName)); if(ortn) { printSslErrStr("SSLSetPeerDomainName", ortn); goto cleanup; } } /* remainder of setup is optional */ if(params->anchorFile) { ortn = sslAddTrustedRoot(ctx, params->anchorFile, params->replaceAnchors); if(ortn) { goto cleanup; } } ortn = sslSetProtocols(ctx, params->acceptedProts, params->tryVersion); if(ortn) { goto cleanup; } if(params->resumeEnable) { ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); if(ortn) { printSslErrStr("SSLSetPeerID", ortn); goto cleanup; } } if(params->disableCertVerify) { ortn = SSLSetEnableCertVerify(ctx, false); if(ortn) { printSslErrStr("SSLSetEnableCertVerify", ortn); goto cleanup; } } if(params->ciphers != NULL) { ortn = sslSetEnabledCiphers(ctx, params->ciphers); if(ortn) { goto cleanup; } } if(params->myCertKcName) { clientCerts = getSslCerts(params->myCertKcName, false, false, NULL, &clientKc); if(clientCerts == nil) { exit(1); } if(params->password) { ortn = SecKeychainUnlock(clientKc, strlen(params->password), (void *)params->password, true); if(ortn) { printf("SecKeychainUnlock returned %d\n", (int)ortn); /* oh well */ } } if(params->idIsTrustedRoot) { /* assume this is a root we want to implicitly trust */ ortn = addIdentityAsTrustedRoot(ctx, clientCerts); if(ortn) { goto cleanup; } } ortn = SSLSetCertificate(ctx, clientCerts); if(ortn) { printSslErrStr("SSLSetCertificate", ortn); goto cleanup; } } do { ortn = SSLHandshake(ctx); if((ortn == errSSLWouldBlock) && !params->silent) { /* keep UI responsive */ sslOutputDot(); } } while (ortn == errSSLWouldBlock); SSLGetClientCertificateState(ctx, ¶ms->certState); SSLGetNegotiatedCipher(ctx, ¶ms->negCipher); SSLGetNegotiatedProtocolVersion(ctx, ¶ms->negVersion); if(ortn != errSecSuccess) { goto cleanup; } /* send a GET msg */ size_t actLen; ortn = SSLWrite(ctx, CLIENT_GETMSG, strlen(CLIENT_GETMSG), &actLen); if(ortn) { printSslErrStr("SSLWrite", ortn); goto cleanup; } #if KEEP_CONNECTED /* * Consume any server data and wait for server to disconnect */ char readBuf[READBUF_LEN]; do { ortn = SSLRead(ctx, readBuf, READBUF_LEN, &actLen); } while (ortn == errSSLWouldBlock); /* convert normal "shutdown" into zero err rtn */ if(ortn == errSSLClosedGraceful) { ortn = errSecSuccess; } #endif /* KEEP_CONNECTED */ cleanup: if(ctx) { OSStatus cerr = SSLClose(ctx); if(ortn == errSecSuccess) { ortn = cerr; } } if(sock) { endpointShutdown(sock); } if(ctx) { SSLDisposeContext(ctx); } params->ortn = ortn; sslThrDebug("Client", "done"); return ortn; }