Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // NSsocket.cpp
- // PAL
- //
- // Created by Erfan on 4/23/14.
- //
- //
- #import <Foundation/Foundation.h>
- #include "NSsocket.h"
- #import "mach/mach.h"
- #include <sys/time.h>
- #include <netdb.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include "PALcritsec.h"
- #include <list>
- #include <CoreFoundation/CFArray.h>
- #include "winEmul.h"
- #include <openssl/ssl.h>
- #include <openssl/bio.h>
- #include <openssl/x509.h>
- #include <openssl/x509_vfy.h>
- #include <openssl/pem.h>
- #include <openssl/x509v3.h>
- #include <openssl/err.h>
- #include <openssl/conf.h>
- #include <fstream>
- #define BUFFER_SIZE 4096
- @interface NSsocket : NSObject <NSStreamDelegate> {
- @private volatile bool m_bConnected;
- @private volatile bool m_bHasSpaceAvailable;
- @private NSInputStream *m_pInputStream;
- @private NSOutputStream *m_pOutputStream;
- @private HANDLE m_hSocketError;
- @private HANDLE m_hSocketOpened;
- @private HANDLE m_hSocketDataReceived;
- @private HANDLE m_hSocketClosed;
- @private PAL::Mutex* m_pReadMutex;
- @private PAL::Mutex* m_pWriteMutex;
- @private std::list<std::string> recvMessageBuffer;
- @private std::list<std::string> sendMessageBuffer;
- }
- -(NSStream *)GetStream:(bool)bInput;
- -(bool)CreateAndConnect:(const std::string&) serverAddress withPort:(const u_int16_t&) serverPort withTransportMode:(bool)bTLS;
- -(void)CloseConnection;
- -(void)CloseStreams;
- -(void)CloseEvents;
- -(bool)IsConnected;
- -(int)Send:(const std::string&)sDataToSend;
- -(int)Recv:(char *)dataReceived withBufferSize:(int)bufferSize;
- -(int)Select:(u_int64_t)uTimeoutValue;
- -(void)Signal;
- @end
- @implementation NSsocket
- - (id)init
- {
- self = [super init];
- if(self)
- {
- // Must be created at the beginning, otherwise calling Select would cause a crash with NULL events
- m_hSocketError = CreateEvent(NULL, FALSE, FALSE, NULL);
- m_hSocketOpened = CreateEvent(NULL, FALSE, FALSE, NULL);
- m_hSocketDataReceived = CreateEvent(NULL, FALSE, FALSE, NULL);
- m_hSocketClosed = CreateEvent(NULL, FALSE, FALSE, NULL);
- m_pReadMutex = new PAL::Mutex;
- m_pWriteMutex = new PAL::Mutex;
- }
- return self;
- }
- -(NSStream *)GetStream:(bool)bInput
- {
- return bInput ? m_pInputStream : m_pOutputStream;
- }
- SecTrustRef changeHostForTrust(SecTrustRef trust)
- {
- CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(
- kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.example.com"));
- CFArrayAppendValue(newTrustPolicies, sslPolicy);
- #ifdef MAC_BACKWARDS_COMPATIBILITY
- /* This technique works in OS X (v10.5 and later) */
- SecTrustSetPolicies(trust, newTrustPolicies);
- CFRelease(oldTrustPolicies);
- return trust;
- #else
- /* This technique works in iOS 2 and later, or
- OS X v10.7 and later */
- CFMutableArrayRef certificates = CFArrayCreateMutable(
- kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- /* Copy the certificates from the original trust object */
- CFIndex count = SecTrustGetCertificateCount(trust);
- CFIndex i=0;
- for (i = 0; i < count; i++) {
- SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);
- CFArrayAppendValue(certificates, item);
- }
- /* Create a new trust object */
- SecTrustRef newtrust = NULL;
- if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {
- /* Probably a good spot to log something. */
- return NULL;
- }
- return newtrust;
- #endif
- }
- std::string gserverAddress;
- int gPort;
- -(bool)CreateAndConnect:(const std::string&) serverAddress withPort:(const u_int16_t&) serverPort withTransportMode:(bool)bTLS
- {
- gserverAddress = serverAddress;
- gPort = serverPort;
- NSLog(@"NSStream %p connecting to: %s:%d in %s", self, serverAddress.c_str(), serverPort, bTLS ? "TLS" : "TCP");
- CFStringRef remoteHost = CFStringCreateWithCString(kCFAllocatorDefault, serverAddress.c_str(), kCFStringEncodingMacRoman);
- CFReadStreamRef readStream;
- CFWriteStreamRef writeStream;
- CFStreamCreatePairWithSocketToHost(NULL, remoteHost, serverPort, &readStream, &writeStream);
- // Set options then bridge to ARC:
- if(bTLS)
- {
- NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
- (id)kCFStreamSocketSecurityLevelTLSv1, kCFStreamPropertySocketSecurityLevel,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
- [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
- nil];
- /*CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings);*/
- // Set this kCFStreamPropertySocketSecurityLevel before
- // setting kCFStreamPropertySSLSettings.
- // Setting kCFStreamPropertySocketSecurityLevel
- // appears to override previous settings in kCFStreamPropertySSLSettings
- CFReadStreamSetProperty(readStream,
- kCFStreamPropertySocketSecurityLevel,
- kCFStreamSocketSecurityLevelTLSv1);
- // this disables certificate chain validation in ssl settings.
- NSDictionary *sslSettings =
- [NSDictionary dictionaryWithObjectsAndKeys:
- (id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
- nil];
- CFReadStreamSetProperty(readStream,
- kCFStreamPropertySSLSettings,
- (__bridge CFDictionaryRef)sslSettings);
- CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings);
- CFReadStreamOpen(readStream);
- CFWriteStreamOpen(writeStream);
- }
- CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
- CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
- m_pInputStream = (__bridge_transfer NSInputStream *)readStream;
- m_pOutputStream = (__bridge_transfer NSOutputStream *)writeStream;
- [m_pInputStream setDelegate:self];
- [m_pOutputStream setDelegate:self];
- recvMessageBuffer.clear();
- sendMessageBuffer.clear();
- m_bConnected = false;
- m_bHasSpaceAvailable = false;
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- dispatch_async(queue, ^ {
- // mainRunLoop used instead of currentRunLoop to avoid a crash on Logout
- [m_pInputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [m_pOutputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [m_pInputStream open];
- [m_pOutputStream open];
- [[NSRunLoop currentRunLoop] run];
- });
- return true;
- }
- -(void)CloseConnection
- {
- NSLog(@"NSStream %p closing connection", self);
- SetEvent(m_hSocketClosed);
- [self CloseStreams];
- }
- -(void)CloseStreams
- {
- m_bConnected = false;
- [m_pInputStream close];
- [m_pOutputStream close];
- [m_pInputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- [m_pOutputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- recvMessageBuffer.clear();
- sendMessageBuffer.clear();
- }
- // Called as the destructor from the CNSsocket's destructor
- -(void)CloseEvents
- {
- CloseEvent(m_hSocketError);
- CloseEvent(m_hSocketOpened);
- CloseEvent(m_hSocketDataReceived);
- CloseEvent(m_hSocketClosed);
- delete m_pReadMutex;
- delete m_pWriteMutex;
- }
- -(bool)IsConnected
- {
- return m_bConnected;
- }
- -(int)Send:(const std::string&)sDataToSend
- {
- PAL::Critical_Section cs(*m_pWriteMutex);
- int iRet = sDataToSend.length();
- if(m_bHasSpaceAvailable){
- m_bHasSpaceAvailable = false;
- //NSLog(@"NSStream %p sending data of length %lu", self, sDataToSend.length());
- iRet = [m_pOutputStream write:(const uint8_t *)sDataToSend.c_str() maxLength:sDataToSend.length()];
- //NSLog(@"NSStream %p sent length %d", self, iRet);
- }
- else{
- sendMessageBuffer.push_back(sDataToSend);
- }
- return iRet;
- }
- -(int)Recv:(char *)dataReceived withBufferSize:(int)bufferSize
- {
- PAL::Critical_Section cs(*m_pReadMutex);
- int dataLength = 0;
- if (recvMessageBuffer.size() > 0)
- {
- std::string buffer = recvMessageBuffer.front();
- recvMessageBuffer.pop_front();
- dataLength = buffer.length() < bufferSize ? buffer.length() : bufferSize;
- memcpy(dataReceived, buffer.c_str(), dataLength);
- // TODO: if buffer.length is greater than bufferSize then the extra data should be stored back so that next time it returns this
- }
- if(dataLength == 0 && [m_pInputStream streamStatus] >= NSStreamStatusClosed)
- {
- NSLog(@"NSStream %p recv error", self);
- return -1;
- }
- return dataLength;
- }
- -(int)Select:(u_int64_t)uTimeoutValue
- {
- int iRet = 0;
- const int iNumEvents = 4;
- HANDLE EventArray[iNumEvents];
- EventArray[0] = m_hSocketError;
- EventArray[1] = m_hSocketOpened;
- EventArray[2] = m_hSocketDataReceived;
- EventArray[3] = m_hSocketClosed;
- DWORD dwWaitStatus = WaitForMultipleObjects(iNumEvents, EventArray, FALSE, uTimeoutValue);
- switch (dwWaitStatus)
- {
- case WAIT_FAILED:
- break;
- case WAIT_OBJECT_0:
- iRet = -1;
- break;
- case WAIT_OBJECT_0 + 1:
- iRet = 1;
- break;
- case WAIT_OBJECT_0 + 2:
- iRet = 1;
- break;
- case WAIT_OBJECT_0 + 3:
- iRet = 0;
- break;
- default:
- break;
- }
- return iRet;
- }
- -(void)Signal
- {
- SetEvent(m_hSocketDataReceived);
- }
- static int openssl_verification(const char cert_filestr[], const char ca_bundlestr[]) {
- OpenSSL_add_all_algorithms();
- ERR_load_BIO_strings();
- ERR_load_crypto_strings();
- BIO *certbio = NULL;
- BIO *cert2bio = NULL;
- X509 *cert = NULL;
- X509 *cert2 = NULL;
- certbio = BIO_new(BIO_s_file());
- int ret = BIO_read_filename(certbio, cert_filestr);
- if(ret != 1) {
- NSLog(@"Certificate Invalid.\n");
- }
- cert = PEM_read_bio_X509(certbio, NULL, 0, NULL);
- BIO_free(certbio);
- cert2bio = BIO_new(BIO_s_file());
- int ret2 = BIO_read_filename(certbio, cert_filestr);
- if(ret2 != 1) {
- NSLog(@"Certificate Invalid.\n");
- }
- cert2 = PEM_read_bio_X509(cert2bio, NULL, 0, NULL);
- BIO_free(cert2bio);
- EVP_PKEY *pkey=X509_get_pubkey(cert2);
- int result = X509_verify(cert, pkey);
- EVP_PKEY_free(pkey);
- X509_free(cert);
- X509_free(cert2);
- if(result > 0) {
- NSLog(@"Certificate Verification Success.\n");
- } else {
- NSLog(@"Certificate Verification Failed.\n");
- }
- return result > 0;
- }
- #pragma mark NSStreamDelegate
- - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
- {
- NSLog(@"Kaidul Islam....\n");
- NSLog(@"NSStream %p eventCode: %d", self, (int)eventCode);
- switch (eventCode) {
- case NSStreamEventNone:
- break;
- case NSStreamEventEndEncountered:
- NSLog(@"NSStreamEventEndEncountered %@", [aStream streamError]);
- [self CloseStreams];
- SetEvent(m_hSocketError);
- break;
- case NSStreamEventErrorOccurred:
- NSLog(@"NSStreamEventErrorOccurred %@", [aStream streamError]);
- [self CloseStreams];
- SetEvent(m_hSocketError);
- break;
- case NSStreamEventHasBytesAvailable:
- {
- if (aStream == m_pInputStream)
- {
- PAL::Critical_Section cs(*m_pReadMutex);
- uint8_t buffer[BUFFER_SIZE];
- NSInteger bytesRead = [m_pInputStream read:buffer maxLength:BUFFER_SIZE];
- if (0 >= bytesRead)
- break;
- NSString *nsStringRead = [[NSString alloc] initWithBytes:buffer length:bytesRead encoding:NSUTF8StringEncoding];
- std::string sStringRead = [nsStringRead UTF8String];
- recvMessageBuffer.push_back(sStringRead);
- SetEvent(m_hSocketDataReceived);
- }
- break;
- }
- case NSStreamEventHasSpaceAvailable:
- {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString *documentsDirectory = [paths objectAtIndex:0];
- NSError *error2 = nil;
- NSString *certPath = [documentsDirectory stringByAppendingPathComponent:@"ss.der"];
- if (![[NSFileManager defaultManager] fileExistsAtPath:certPath])
- {
- NSLog(@"File Doesn't exist");
- }else {
- NSLog(@"Certificate Path %@\n",certPath);
- }
- NSData * certData = nil;
- certData = [NSData dataWithContentsOfFile: certPath options: 1 error: &error2];
- if (error2 || !certData) {
- NSLog(@"Read failed with error: %@", [error2 localizedDescription]);
- }
- else
- {
- NSLog(@"Certificate Data: %@\n",certData);
- }
- error2 = nil;
- SecCertificateRef cert = (__bridge SecCertificateRef)[NSData dataWithContentsOfFile: certPath options: 1 error: &error2];
- if(error2 || !cert)
- {
- NSLog(@"Certificate Nil: %@",[error2 localizedDescription]);
- }
- else
- {
- NSLog(@"Certificate is okay: %@", cert);
- }
- ///////////////////////////////////////////1. Create Policy////////////////////////////////
- SecPolicyRef policy = SecPolicyCreateSSL(NO, CFSTR("https://www.smartbabymonitor.ugrow.philips.com"));
- if(policy == NULL)
- {
- NSLog(@"Policy is NULL");
- }
- else
- {
- NSLog(@"Policy not NULL: %@", policy);
- }
- CFArrayRef streamCertificates = (CFArrayRef)CFBridgingRetain([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]);
- if(streamCertificates == NULL)
- {
- NSLog(@"NULL streamCertificates");
- }
- else
- {
- NSLog(@"StreamCertificates is not NULL. Server CERTIFICATES: %@", streamCertificates);
- }
- //////////////////////////////////3. Create trust////////////////////////////////////////////
- SecTrustRef trust = NULL;
- OSStatus status = SecTrustCreateWithCertificates(streamCertificates, policy, &trust);
- if (status != noErr || trust == NULL) {
- NSLog(@"could not create a trust management object!");
- }
- else
- {
- NSLog(@"StreamCertificates is created: %@", trust);
- }
- NSLog(@"SecTrustCreateWithCertificates STATUS code :%d\n", (int)status); // 0 = errSecSuccess
- /////////////////////////////4. Set Anchor///////////////////////////////////////
- CFArrayRef certArrayRef = CFArrayCreate(NULL, ( const void **)(&cert), (CFIndex) 1, NULL);
- status = SecTrustSetAnchorCertificates(trust, certArrayRef);
- if (status != noErr || trust == NULL) {
- NSLog(@"could not SecTrustSetAnchorCertificates");
- }
- else
- {
- // NSLog(@"SecTrustSetAnchorCertificates: %@", certArrayRef);
- }
- // #5
- NSLog(@"SecTrustSetAnchorCertificates STATUS code :%d\n", (int)status);
- /////////////////////////5. Verify Trust////////////////////////////////////
- SecTrustResultType trustResultType = kSecTrustResultUnspecified;
- // NSLog(@"trustResultType: %d", (int) trustResultType);
- status = SecTrustEvaluate(trust, &trustResultType); // issue
- // NSLog(@"status = %d, trustResultType = %d", (int) status, (int) trustResultType);
- switch (trustResultType) {
- case kSecTrustResultProceed: // 1
- NSLog(@"kSecTrustResultProceed");
- break;
- case kSecTrustResultConfirm: // 2 - deprecated in iOS 7, but still valid in iOS 6
- NSLog(@"kSecTrustResultConfirm");
- break;
- case kSecTrustResultUnspecified: // 4
- NSLog(@"kSecTrustResultUnspecified");
- break;
- case kSecTrustResultRecoverableTrustFailure: // 5
- NSLog(@"kSecTrustResultRecoverableTrustFailure");
- break;
- case kSecTrustResultDeny: // 3
- NSLog(@"kSecTrustResultDeny");
- break;
- case kSecTrustResultFatalTrustFailure: // 6
- NSLog(@"kSecTrustResultFatalTrustFailure");
- break;
- case kSecTrustResultOtherError: // 7
- NSLog(@"kSecTrustResultOtherError");
- break;
- case kSecTrustResultInvalid: // 0
- NSLog(@"kSecTrustResultInvalid");
- break;
- default:
- NSLog(@"default");
- break;
- }
- if (status == errSecSuccess) {
- // expect trustResultType == kSecTrustResultUnspecified
- // until my cert exists in the keychain see technote for more detail.
- if (trustResultType == kSecTrustResultUnspecified) {
- NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType);
- } else {
- NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType);
- }
- } else {
- NSLog(@"Creating trust failed: %d", (int) status);
- [aStream close];
- }
- NSLog(@".......................................................\n\n\n\n");
- if (trust) {
- CFRelease(trust);
- NSLog(@"Trust Released.\n");
- }
- if (policy) {
- CFRelease(policy);
- NSLog(@"Policy Released.\n");
- }
- if(aStream == m_pOutputStream)
- {
- PAL::Critical_Section cs(*m_pWriteMutex);
- m_bHasSpaceAvailable = true;
- if(sendMessageBuffer.size() > 0)
- {
- std::string sDataToSend = sendMessageBuffer.front();
- sendMessageBuffer.pop_front();
- int iWritten = 0;
- if(sDataToSend.length() > 0)
- {
- m_bHasSpaceAvailable = false;
- iWritten = [m_pOutputStream write:(const uint8_t *)sDataToSend.c_str() maxLength:sDataToSend.length()];
- }
- }
- }
- // SetEvent(m_hSocketOpened);
- }
- break;
- case NSStreamEventOpenCompleted:
- NSLog(@"NSStreamEventOpenCompleted");
- m_bConnected = true;
- //SetEvent(m_hSocketOpened);
- break;
- default:
- //NSLog(@"NSStream default");
- break;
- }
- }
- @end
- /**********************************************************
- CNSsocket implementation
- **********************************************************/
- CNSsocket::CNSsocket()
- {
- m_pNSsocket = [[NSsocket alloc] init];
- }
- CNSsocket::~CNSsocket()
- {
- CloseConnection();
- [m_pNSsocket CloseEvents];
- }
- // [TODO] we MUST check the connection status
- bool CNSsocket::CreateAndConnect(const std::string& serverAddress, const u_int16_t& serverPort, bool bTLS)
- {
- return [m_pNSsocket CreateAndConnect:serverAddress withPort:serverPort withTransportMode:bTLS];
- }
- void CNSsocket::GetLocalAddressAndPort(std::string& serverAddress, u_int16_t& serverPort)
- {
- CFWriteStreamRef writeStream = (__bridge CFWriteStreamRef) [m_pNSsocket GetStream:false];
- // Get the native socket handle:
- CFTypeRef socketTypeRef = CFWriteStreamCopyProperty(writeStream, kCFStreamPropertySocketNativeHandle);
- if(socketTypeRef == NULL)
- {
- NSLog(@"Error in getting native socket handle");
- serverAddress = "";
- serverPort = 0;
- return;
- }
- CFDataRef socketData = (CFDataRef)socketTypeRef;
- CFSocketNativeHandle socket;
- CFDataGetBytes(socketData, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8 *)&socket);
- // Get the local socket address from the socket handle:
- struct sockaddr_storage sa;
- socklen_t salen = sizeof(sa);
- getsockname(socket, (struct sockaddr *)&sa, &salen);
- // Get numeric host and port from socket address:
- char host[NI_MAXHOST];
- char service[NI_MAXSERV];
- getnameinfo((struct sockaddr *)&sa, salen, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST|NI_NUMERICSERV);
- serverAddress = std::string(host);
- serverPort = (u_int16_t)atoi(service);
- NSLog(@"Local address: %s, local port: %d", serverAddress.c_str(), serverPort);
- }
- void CNSsocket::CloseConnection()
- {
- [m_pNSsocket CloseConnection];
- }
- int CNSsocket::Send(const char* dataToSend, int sizeOfData)
- {
- return [m_pNSsocket Send:std::string(dataToSend, sizeOfData)];
- }
- int CNSsocket::Recv(char dataReceived[], int bufferSize)
- {
- return [m_pNSsocket Recv:dataReceived withBufferSize:bufferSize];
- }
- // [TODO] We should use callback instead of select() to reduce processing cost.
- // This needs to be called after invoking CreateAndConnect to get connection status.
- int CNSsocket::Select(u_int64_t timeOutValue)
- {
- return [m_pNSsocket Select:timeOutValue];
- }
- int CNSsocket::WaitForConnect(u_int64_t timeOutValue)
- {
- u_int64_t slept = 0;
- const u_int64_t duration = 50;
- while(slept < timeOutValue){
- if([m_pNSsocket IsConnected])
- return 1;
- usleep(duration * 1000);
- slept += duration;
- }
- return [m_pNSsocket IsConnected] ? 1 : -1;
- }
- void CNSsocket::Signal()
- {
- [m_pNSsocket Signal];
- }
Advertisement
Add Comment
Please, Sign In to add comment