Advertisement
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 "winEmul.h"
- #ifdef PHILIPS
- #define VERIFY_CERTIFICATE
- #endif
- #ifdef VERIFY_CERTIFICATE
- #include <CoreFoundation/CFArray.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>
- #endif
- #define BUFFER_SIZE 4096
- @interface NSsocket : NSObject <NSStreamDelegate> {
- @private volatile bool m_bConnected;
- @private volatile bool m_bHasSpaceAvailable;
- #ifdef VERIFY_CERTIFICATE
- @private volatile bool m_certificateChecked;
- #endif
- @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;
- }
- -(bool)CreateAndConnect:(const std::string&) serverAddress withPort:(const u_int16_t&) serverPort withTransportMode:(bool)bTLS
- {
- 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);
- #ifdef VERIFY_CERTIFICATE
- m_certificateChecked = YES;
- #endif
- // Set options then bridge to ARC:
- if(bTLS)
- {
- #ifdef VERIFY_CERTIFICATE
- m_certificateChecked = NO;
- #endif
- #ifndef VERIFY_CERTIFICATE
- NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
- (id)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamPropertySocketSecurityLevel,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
- [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
- nil];
- CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings);
- CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, (__bridge CFDictionaryRef)settings);
- #else
- // Set this kCFStreamPropertySocketSecurityLevel before setting kCFStreamPropertySSLSettings.
- // Setting kCFStreamPropertySocketSecurityLevel appears to override previous settings in kCFStreamPropertySSLSettings
- CFReadStreamSetProperty(readStream,
- kCFStreamPropertySocketSecurityLevel,
- kCFStreamSocketSecurityLevelNegotiatedSSL);
- // 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)sslSettings);
- CFReadStreamOpen(readStream);
- CFWriteStreamOpen(writeStream);
- #endif
- }
- 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);
- }
- #ifdef VERIFY_CERTIFICATE
- #define NS_ASSERT(X, ...) \
- NSAssert(X, __VA_ARGS__); \
- if(!(X)){ \
- NSLog(__VA_ARGS__); \
- return NO; \
- }
- /*
- * Returns the leaf certificate from a SecTrust object
- * (that is always the certificate at index 0)
- */
- static SecCertificateRef SecTrustGetLeafCertificate(SecTrustRef trust)
- {
- SecCertificateRef result = NULL;
- if(trust == NULL){
- NSLog(@"trust in null");
- return NULL;
- }
- if (SecTrustGetCertificateCount(trust) > 0) {
- result = SecTrustGetCertificateAtIndex(trust, 0);
- if(result == NULL){
- NSLog(@"result in null");
- return NULL;
- }
- }
- return result;
- }
- -(BOOL) VerifyCertificate: (NSStream *)aStream
- {
- OpenSSL_add_all_algorithms();
- OpenSSL_add_all_ciphers();
- OpenSSL_add_all_digests();
- ERR_load_BIO_strings();
- ERR_load_crypto_strings();
- NSData *certData = nil;
- BOOL result = NO;
- X509 *root_certificate_X509 = NULL;
- X509 *server_certificate_X509 = NULL;
- EVP_PKEY *publicKey = NULL;
- SecCertificateRef secCertRef = NULL;
- SecPolicyRef policyRef = NULL;
- SecTrustRef trustRef = NULL;
- CFDataRef cfCertRef = NULL;
- CFArrayRef streamCertRef = NULL;
- NSString *root_certificate_name = @"philips_trusted_cert";
- NSString *root_certificate_extension = @"der";
- NSString *serverAddrss = @"www.smartbabymonitor.ugrow.philips.com";
- /** Reading root cetificate **/
- NSBundle *bundle = [NSBundle bundleForClass:[self class]];
- certData = [NSData dataWithContentsOfFile:[bundle pathForResource: root_certificate_name ofType: root_certificate_extension]];
- NS_ASSERT(certData != nil, @"certData failed.");
- /** root certificate to X509 object **/
- const unsigned char *rootCertificateDataBytes = (const unsigned char *)[certData bytes];
- root_certificate_X509 = d2i_X509(NULL, &rootCertificateDataBytes, [certData length]);
- NS_ASSERT(root_certificate_X509 != NULL, @"root_certificate_X509 failed.");
- /** Create Policy **/
- policyRef = SecPolicyCreateSSL(NO, (__bridge CFStringRef) serverAddrss);
- NS_ASSERT(policyRef != NULL, @"policyRef failed.");
- /** Create server certificate from stream **/
- streamCertRef = (CFArrayRef)CFBridgingRetain([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates]);
- NS_ASSERT(streamCertRef != NULL, @"streamCertRef failed.");
- /** Create Trust **/
- OSStatus status = SecTrustCreateWithCertificates(streamCertRef, policyRef, &trustRef);
- NS_ASSERT(status == errSecSuccess, @"status failed.");
- NS_ASSERT(trustRef != NULL, @"Could not create a trust management object!");
- /** intermediate manipulation. certificate ref from trust object and convert into NSData **/
- secCertRef = SecTrustGetLeafCertificate(trustRef);
- NS_ASSERT(secCertRef != NULL, @"secCertRef failed.");
- cfCertRef = SecCertificateCopyData(secCertRef);
- NS_ASSERT(cfCertRef != NULL, @"cfCertRef failed.");
- certData = nil;
- certData = (__bridge NSData *) cfCertRef;
- NS_ASSERT(certData != nil, @"certData failed.");
- /** server certificate to X509 object **/
- const unsigned char *serverCertificateDataBytes = (const unsigned char *)[certData bytes];
- server_certificate_X509 = d2i_X509(NULL, &serverCertificateDataBytes, [certData length]);
- NS_ASSERT(server_certificate_X509 != NULL, @"server_certificate_X509 failed.");
- /** openSSL certificate verification **/
- publicKey = X509_get_pubkey(root_certificate_X509);
- NS_ASSERT(publicKey != NULL, @"Public Key failed.");
- result = X509_verify(server_certificate_X509, publicKey);
- /** releasing memory **/
- EVP_PKEY_free(publicKey);
- X509_free(root_certificate_X509);
- X509_free(server_certificate_X509);
- if(streamCertRef) CFRelease(streamCertRef);
- if(cfCertRef) CFRelease(cfCertRef);
- if(trustRef) CFRelease(trustRef);
- if(policyRef) CFRelease(policyRef);
- return result == 1;
- }
- #endif
- #pragma mark -
- #pragma mark NSStreamDelegate
- - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
- {
- // NSLog(@"NSStream %p eventCode: %d", self, (int)eventCode);
- switch (eventCode) {
- case NSStreamEventNone:
- // do nothing.
- //NSLog(@"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];
- //NSLog(@"NSStreamEventHasBytesAvailable of length %d", bytesRead);
- if (0 >= bytesRead)
- break;
- NSString *nsStringRead = [[NSString alloc] initWithBytes:buffer length:bytesRead encoding:NSUTF8StringEncoding];
- std::string sStringRead = [nsStringRead UTF8String];
- NSLog(@"recieved data: %s", sStringRead.c_str());
- recvMessageBuffer.push_back(sStringRead);
- SetEvent(m_hSocketDataReceived);
- }
- break;
- }
- case NSStreamEventHasSpaceAvailable:
- {
- #ifdef VERIFY_CERTIFICATE
- if (!m_certificateChecked) {
- m_certificateChecked = YES;
- if ([self VerifyCertificate: aStream]) {
- NSLog(@"Certificate Verification Success.");
- } else
- {
- NSLog(@"Certificate Verification Failed.");
- [self CloseStreams];
- SetEvent(m_hSocketError);
- break;
- }
- }
- #endif
- 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;
- //NSLog(@"NSStream %p sending data of length %lu", self, sDataToSend.length());
- iWritten = [m_pOutputStream write:(const uint8_t *)sDataToSend.c_str() maxLength:sDataToSend.length()];
- //NSLog(@"NSStream %p sent length %d", self, iWritten);
- }
- }
- }
- // 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
Advertisement