giovedì 16 aprile 2015

Swift SSL connection

Una classe in Swift per effettuare connessioni client utilizzando un socket SSL

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//
//  SSLConnection.swift
//  MacSSLTest
//
//  Created by Michel on 16/04/15.
//  Copyright (c) 2015 DMD Medtech. All rights reserved.
//

import Foundation

protocol SSLConnectionDelegate {
    func received(msg: String)
}

class SSLConnection : NSObject, NSStreamDelegate
{
    private var hostName: CFString
    private var port: UInt32
    
    private var readStream: NSInputStream!
    private var writeStream: NSOutputStream!
    
    var delegate: SSLConnectionDelegate?
    
    private var bufferedMsg = ""
    
    init(host: CFString, port p: UInt32) {
        hostName = host
        port = p
    }
    
    func stream(stream: NSStream, handleEvent eventCode: NSStreamEvent) {
        switch (eventCode) {
        case NSStreamEvent.OpenCompleted:
            NSLog("Stream opened")
            break
        case NSStreamEvent.HasBytesAvailable:
            NSLog("HasBytesAvailable")
            var buffer = [UInt8](count: 4096, repeatedValue: 0)
            if stream == readStream {
                while readStream.hasBytesAvailable {
                    var len = readStream.read(&buffer, maxLength: buffer.count)
                    if len > 0 {
                        for i in 0...len {
                            let c = UnicodeScalar(buffer[i])
                            if c.value == 10 || c.value == 13 {
                                if count(bufferedMsg) > 0 {
                                    delegate?.received(bufferedMsg)
                                    self.bufferedMsg = ""
                                }
                            }
                            else {
                                self.bufferedMsg.append(c)
                            }
                        }
                        var output = NSString(bytes: &buffer, length: len, encoding: NSUTF8StringEncoding)
                        if output != "" {
                            NSLog("server said: %@", output!)
                        }
                    }
                }
            }
            break
        case NSStreamEvent.ErrorOccurred:
            NSLog("ErrorOccurred")
            break
        case NSStreamEvent.EndEncountered:
            NSLog("EndEncountered")
            break
        default:
            NSLog("unknown.")
        }
    }
    
    func writeString(msg: String) -> Bool {
        let messageWithFlush = msg + "\n"
        let data: NSData = messageWithFlush.dataUsingEncoding(NSUTF8StringEncoding)!
        let buf = UnsafePointer<UInt8>(data.bytes)
        println("Scrivo \(data.length) bytes...")
        let result = writeStream?.write(buf, maxLength: data.length)
        if let r = result {
            println("Result of write: \(r)")
            return r != 0
        }
        return false
    }
    
    func connect() -> Bool {
        var rs: Unmanaged<CFReadStream>?
        var ws: Unmanaged<CFWriteStream>?
        
        CFStreamCreatePairWithSocketToHost(nil, hostName as CFString, port, &rs, &ws)
        let conf = ["kCFStreamSSLAllowsExpiredCertificates" : NSNumber(bool: true),
            "kCFStreamSSLAllowsExpiredRoots" : NSNumber(bool: true),
            "kCFStreamSSLAllowsAnyRoot" : NSNumber(bool: true),
            "kCFStreamSSLValidatesCertificateChain" : NSNumber(bool: false)
        ]
        
        let retainedWs = ws?.takeRetainedValue()
        let retainedRs = rs?.takeRetainedValue()
        
        let resWP = CFWriteStreamSetProperty(retainedWs, kCFStreamPropertySSLSettings, conf as CFTypeRef)
        let resRP = CFReadStreamSetProperty(retainedRs, kCFStreamPropertySSLSettings, conf as CFTypeRef)
        
        self.readStream = retainedRs
        self.writeStream = retainedWs
        
        self.readStream.delegate = self
        self.writeStream.delegate = self
        
        self.readStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        self.writeStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
        
        self.readStream.open()
        self.writeStream.open()
        
        return true
    }
}