can someone tell me how to find out in Swift2 if Port is busy?
Because I write a mac app that has a self written Tcp server, but sometimes it wont start up because it "cant bind to port". So how can I check if the port is not used, to block the start button of the Tcp server until the port is free, again?
And I do not want a new framework.
Thanks
mainly code taken from Swifter: https://github.com/glock45/swifter
func checkTcpPortForListen(port: in_port_t) -> (Bool, descr: String){
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return (false, "SocketCreationFailed, \(descriptionOfLastError())")
}
var addr = sockaddr_in()
addr.sin_len = __uint8_t(sizeof(sockaddr_in))
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeof(sockaddr_in)))
if bind(socketFileDescriptor, &bind_addr, socklen_t(sizeof(sockaddr_in))) == -1 {
let details = descriptionOfLastError()
release(socketFileDescriptor)
return (false, "\(port), BindFailed, \(details)")
}
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
let details = descriptionOfLastError()
release(socketFileDescriptor)
return (false, "\(port), ListenFailed, \(details)")
}
release(socketFileDescriptor)
return (true, "\(port) is free for use")
}
func release(socket: Int32) {
Darwin.shutdown(socket, SHUT_RDWR)
close(socket)
}
func descriptionOfLastError() -> String {
return String.fromCString(UnsafePointer(strerror(errno))) ?? "Error: \(errno)"
}
Update of the correct answer for Swift 4:
func checkTcpPortForListen(port: in_port_t) -> (Bool, descr: String) {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return (false, "SocketCreationFailed, \(descriptionOfLastError())")
}
var addr = sockaddr_in()
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockkAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
let details = descriptionOfLastError()
release(socket: socketFileDescriptor)
return (false, "\(port), BindFailed, \(details)")
}
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
let details = descriptionOfLastError()
release(socket: socketFileDescriptor)
return (false, "\(port), ListenFailed, \(details)")
}
release(socket: socketFileDescriptor)
return (true, "\(port) is free for use")
}
func release(socket: Int32) {
Darwin.shutdown(socket, SHUT_RDWR)
close(socket)
}
func descriptionOfLastError() -> String {
return String.init(cString: (UnsafePointer(strerror(errno))))
}
EDIT:
example for calling this function:
var portNum: UInt16 = 0
for i in 50000..<65000 {
let (isFree, _) = checkTcpPortForListen(port: UInt16(i))
if isFree == true {
portNum = UInt16(i)
break;
}
}
Related
trying to check whether port open or not in iOS, swift4
socket(AF_INET, SOCK_STREAM, 0)
gives the error: Use of unresolved identifier 'socket'
func isPortOpen(port: in_port_t) -> Bool {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return false
}
var addr = sockaddr_in()
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockkAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
return false
}
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
return false
}
return true
}
In my application want to check port is open or not. Here refer this link
iOS SDK: How can I check if a port is open?
But dnt get any solution. And also refer these two github source,
https://github.com/swiftsocket/SwiftSocket
https://github.com/robbiehanson/CocoaAsyncSocket
But dnt any solution. Any one help how check port is open or not. Thanks Advance.
Please use the below method to port is open or not
func isPortOpen(port: in_port_t) -> Bool {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return false
}
var addr = sockaddr_in()
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockkAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
return false
}
if listen(socketFileDescriptor, SOMAXCONN ) == -1 {
return false
}
return true
}
Use like:
let port = UInt16(10000)
print(isPortOpen(port:port))
I cannot add comments, so I am adding this as a separate answer. I followed Surani's answer, but encountered errors when using the port later in the application. I found I had to manually close the socket before returning the response.
So I modified the last 4 lines to
let isOpen = listen(socketFileDescriptor, SOMAXCONN ) != -1
Darwin.close(socketFileDescriptor)
return isOpen
Here is the revised version of Surani's answer with the socket closing code
func isPortOpen(port: in_port_t) -> Bool {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return false
}
var addr = sockaddr_in()
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockkAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
return false
}
let isOpen = listen(socketFileDescriptor, SOMAXCONN ) != -1
Darwin.close(socketFileDescriptor)
return isOpen
}
Use like:
let port = UInt16(10000)
print(isPortOpen(port:port))
I am using IDZSwiftCommonCrypto for image encryption using StreamCryptor described as an example at its GitHub page: https://github.com/iosdevzone/IDZSwiftCommonCrypto
I am not able to successfully decrypt. Here is my code for encryption and decryption (imageData comes from UIImageView). The output is different from input after encryption (imageData is different from xx).
Encryption:
func performImageEncryption(imageData: Data) -> Void {
var inputStream = InputStream(data: imageData)
let key = arrayFrom(hexString: "2b7e151628aed2a6abf7158809cf4f3c")
var sc = StreamCryptor(operation:.encrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:Array<UInt8>())
var inputBuffer = Array<UInt8>(repeating:0, count:1024)
var outputBuffer = Array<UInt8>(repeating:0, count:1024)
inputStream.open()
var cryptedBytes = 0
var xx = Data()
var count = 0
while inputStream.hasBytesAvailable
{
count = count + 1024
let bytesRead = inputStream.read(&inputBuffer, maxLength: inputBuffer.count)
let status = sc.update(bufferIn: inputBuffer, byteCountIn: bytesRead, bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
xx.append(contentsOf: outputBuffer)
}
let status = sc.final(bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
xx.append(contentsOf: outputBuffer)
inputStream.close()
performImageDecryption(encryptedImageData: xx)
}
Decryption:
func performImageDecryption(encryptedImageData: Data) -> Void {
let key = arrayFrom(hexString: "2b7e151628aed2a6abf7158809cf4f3c")
var sc = StreamCryptor(operation:.decrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:Array<UInt8>())
var inputStreamD = InputStream(data: encryptedImageData)
var inputBuffer = Array<UInt8>(repeating:0, count:1024)
var outputBuffer = Array<UInt8>(repeating:0, count:1024)
inputStreamD.open()
var cryptedBytes = 0
var xx = Data()
while inputStreamD.hasBytesAvailable
{
let bytesRead = inputStreamD.read(&inputBuffer, maxLength: inputBuffer.count)
let status = sc.update(bufferIn: inputBuffer, byteCountIn: bytesRead, bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
xx.append(contentsOf: outputBuffer)
}
let status = sc.final(bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
xx.append(contentsOf: outputBuffer)
inputStreamD.close()
}
xx.append(outputBuffer, count: cryptedBytes)
should help.
below is sample code for picking up an encrypted image file and returning the data.
func decryptImage(from path:URL)-> Data? {
var decryptData = Data()
let sc = StreamCryptor(operation:.decrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:iv)
guard let encryptedInputStream = InputStream(fileAtPath: path.relativePath) else {
return nil
}
var inputBuffer = [UInt8](repeating: 0, count: Int(1024))
var outputBuffer = [UInt8](repeating: 0, count: Int(1024))
encryptedInputStream.open()
var cryptedBytes : Int = 0
while encryptedInputStream.hasBytesAvailable
{
let bytesRead = encryptedInputStream.read(&inputBuffer, maxLength: inputBuffer.count)
let status = sc.update(bufferIn: inputBuffer, byteCountIn: bytesRead, bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
if (status != Status.success) {
encryptedInputStream.close()
return nil
}
if(cryptedBytes > 0)
{
decryptData.append(outputBuffer, count: cryptedBytes)
}
}
let status = sc.final(bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
if (status != Status.success) {
encryptedInputStream.close()
return nil
}
if(cryptedBytes > 0)
{
decryptData.append(outputBuffer, count: cryptedBytes)
}
encryptedInputStream.close()
return decryptData
}
Happy coding :)
Just trying Bonjour in swift 3
Here is my code , I can receive the delegate
func netServiceDidResolveAddress(_ sender: NetService) {
print("netServiceDidResolveAddress service name \(sender.name) of type \(sender.type)," +
"port \(sender.port), addresses \(sender.addresses)")
}
And here is my result
netServiceDidResolveAddress service name Webber's Mac mini of type
_myapp._tcp.,port 5678, addresses Optional([<1002162e c0a80205 00000000 00000000>, <1c1e162e 00000000 fe800000 00000000 00bce7ad
24b4b7e8 08000000>])
c0a80205 is the IP I looking for => 192.168.2.5
And the address is [Data] , Apple says
The addresses of the service. This is an NSArray of NSData instances,
each of which contains a single struct sockaddr suitable for use with
connect(2). In the event that no addresses are resolved for the
service or the service has not yet been resolved, an empty NSArray is
returned.
I still confuse why Data can't use .btyes ?
As Apple says "This is an NSArray of NSData instances"
But I can't use it like NSData
And how to resolve the address as readable IP string ?
I try this before , but do not get the result as I except ...
let thedata = NSData(bytes: sender.addresses, length: (sender.addresses?.count)!)
var storage = sockaddr_storage()
thedata.getBytes(&storage, length: sizeof(sockaddr_storage))
if Int32(storage.ss_family) == AF_INET {
let addr4 = withUnsafePointer(&storage) {UnsafePointer<sockaddr_in>($0).pointee }
print(inet_ntoa(addr4.sin_addr));
}
Any suggestion will be help , Thanks
Here's how I did it in Swift 3.
func netServiceDidResolveAddress(_ sender: NetService) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
guard let data = sender.addresses?.first else { return }
data.withUnsafeBytes { (pointer:UnsafePointer<sockaddr>) -> Void in
guard getnameinfo(pointer, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return
}
}
let ipAddress = String(cString:hostname)
print(ipAddress)
}
Swift 5
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
data.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Void in
let sockaddrPtr = pointer.bindMemory(to: sockaddr.self)
guard let unsafePtr = sockaddrPtr.baseAddress else { return }
guard getnameinfo(unsafePtr, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return
}
}
let ipAddress = String(cString:hostname)
print(ipAddress)
Edited Phil Coles answer for Swift 5.0 warning free solution:
func netServiceDidResolveAddress(_ sender: NetService) {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
guard let data = sender.addresses?.first else { return }
data.withUnsafeBytes { ptr in
guard let sockaddr_ptr = ptr.baseAddress?.assumingMemoryBound(to: sockaddr.self) else {
// handle error
return
}
var sockaddr = sockaddr_ptr.pointee
guard getnameinfo(sockaddr_ptr, socklen_t(sockaddr.sa_len), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
return
}
}
let ipAddress = String(cString:hostname)
print(ipAddress)
}
OK ... this is not a smart answer , at least I can get the readable IP
Just use this func to get IP string
let bonjourDevices = [NetService]()
let bonjourDevice = bonjourDevices[0]
let host = self.getIPV4StringfromAddress(address:bonjourDevice.addresses!)
func getIPV4StringfromAddress(address: [Data] , port : Int ) -> String{
let data = address.first! as NSData;
var ip1 = UInt8(0)
data.getBytes(&ip1, range: NSMakeRange(4, 1))
var ip2 = UInt8(0)
data.getBytes(&ip2, range: NSMakeRange(5, 1))
var ip3 = UInt8(0)
data.getBytes(&ip3, range: NSMakeRange(6, 1))
var ip4 = UInt8(0)
data.getBytes(&ip4, range: NSMakeRange(7, 1))
let ipStr = String(format: "%d.%d.%d.%d:%d",ip1,ip2,ip3,ip4,port);
return ipStr;
}
I can't make it work with Data, but using NSData, I would use this:
extension NSData {
func castToCPointer<T>() -> T {
let mem = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
self.getBytes(mem, length: MemoryLayout<T.Type>.size)
return mem.move()
}
}
So we have netServiceDidResolveAddress:
func netServiceDidResolveAddress(_ sender: NetService) {
if let addresses = sender.addresses, addresses.count > 0 {
for address in addresses {
let data = address as NSData
let inetAddress: sockaddr_in = data.castToCPointer()
if inetAddress.sin_family == __uint8_t(AF_INET) {
if let ip = String(cString: inet_ntoa(inetAddress.sin_addr), encoding: .ascii) {
// IPv4
print(ip)
}
} else if inetAddress.sin_family == __uint8_t(AF_INET6) {
let inetAddress6: sockaddr_in6 = data.castToCPointer()
let ipStringBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
var addr = inetAddress6.sin6_addr
if let ipString = inet_ntop(Int32(inetAddress6.sin6_family), &addr, ipStringBuffer, __uint32_t(INET6_ADDRSTRLEN)) {
if let ip = String(cString: ipString, encoding: .ascii) {
// IPv6
print(ip)
}
}
ipStringBuffer.deallocate(capacity: Int(INET6_ADDRSTRLEN))
}
}
}
}
I am having the following result (storing ips in array before display):
["172.16.10.120", "172.16.8.251", "::", "::82c9:d9a5:2eed:1c87"]
Code inspired by https://gist.github.com/agrippa1994/d8c66a2ded74fb2dd801 written in Swift 2.3 and adapted for Swift 3.0
I'm trying to use Secure Transport with BSD sockets using Swift. It seems like it should be simple enough, but I can't get it to work and documentation on the subject is scarce.
I've boiled my issue down to a simple "Socket" class, where I've (to the best of my knowledge) fulfilled the requirements of Secure Transport.
import Cocoa
class Socket: NSObject {
private let hello = "Hello!"
private var socketfd: Int32
private var sock_addr: sockaddr
private var sslContext: SSLContext?
var sslWriteCallbackFunc: SSLWriteFunc {
get {
let ump = UnsafeMutablePointer<((SSLConnectionRef, UnsafePointer<Void>,
UnsafeMutablePointer<Int>) -> OSStatus)>.alloc(1)
ump.initialize(sslWriteCallback)
return CFunctionPointer<((SSLConnectionRef, UnsafePointer<Void>,
UnsafeMutablePointer<Int>) -> OSStatus)>(COpaquePointer(ump))
}
}
var sslReadCallbackFunc: SSLReadFunc {
get {
let ump = UnsafeMutablePointer<((SSLConnectionRef, UnsafeMutablePointer<Void>,
UnsafeMutablePointer<Int>) -> OSStatus)>.alloc(1)
ump.initialize(sslReadCallback)
return CFunctionPointer<((SSLConnectionRef, UnsafeMutablePointer<Void>,
UnsafeMutablePointer<Int>) -> OSStatus)>(COpaquePointer(ump))
}
}
init(address: String, port: UInt16) {
socketfd = Darwin.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
var addr = Darwin.sockaddr_in(sin_len: __uint8_t(sizeof(sockaddr_in)), sin_family: sa_family_t(AF_INET), sin_port: CFSwapInt16(port), sin_addr: in_addr(s_addr: inet_addr(address)), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
sock_addr = Darwin.sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
Darwin.memcpy(&sock_addr, &addr, Int(sizeof(sockaddr_in)))
super.init()
}
func connect() -> Socket {
let err = Darwin.connect(socketfd, &sock_addr, socklen_t(sizeof(sockaddr_in)))
return self
}
func makeSecure() -> Socket {
if let umc = SSLCreateContext(nil, kSSLClientSide, kSSLStreamType) {
sslContext = umc.takeRetainedValue()
var status = SSLSetIOFuncs(sslContext!, sslReadCallbackFunc, sslWriteCallbackFunc)
status = SSLSetConnection(sslContext!, &socketfd)
SSLHandshake(sslContext!)
}
return self
}
func sendHello() -> Socket {
let bytes = [UInt8](hello.utf8)
let data = NSData(bytes: bytes, length: bytes.count)
let test = UnsafeMutablePointer<Int>.alloc(1)
test.initialize(bytes.count)
self.sslWriteCallback(&socketfd, data: data.bytes, dataLength: test)
return self
}
// MARK: - SSL Callback Methods
func sslReadCallback(connection: SSLConnectionRef,
data: UnsafeMutablePointer<Void>,
dataLength: UnsafeMutablePointer<Int>) -> OSStatus {
let bytesRead = read(socketfd, data, UnsafePointer<Int>(dataLength).memory)
return noErr
}
func sslWriteCallback(connection: SSLConnectionRef,
data: UnsafePointer<Void>,
dataLength: UnsafeMutablePointer<Int>) -> OSStatus {
let sent = Darwin.sendto(socketfd, data, UnsafePointer<Int>(dataLength).memory, 0, &sock_addr, socklen_t(sizeof(sockaddr_in)))
if (sent < 0) {
let error = NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil)
println(error.localizedDescription)
} else {
println("Sent \(sent) bytes (\(hello))")
}
return noErr
}
}
I've tested the non-TLS socket communication by making a simple instance:
let socket = Socket(address: "some-ip-address", port: 8080)
socket.connect().sendHello()
and running an echo server on the target machine using netcat. This works fine.
nc -l -p 8080
Trying to wrap the socket in Secure Transport's TLS (calling the makeSecure() method) crashes at the call to SSLHandshake(...) with a EXC_BAD_ADDRESS(code=2, address=...) error. Does anyone have any pointers as to what it is I'm missing here?
EDIT
I can see Console puts out:
04/06/15 09:20:48,000 kernel[0]: Data/Stack execution not permitted: TheProject[pid 29184] at virtual address 0x100602000, protections were read-write
EDIT 2
I got it working with Swift 2 in the Xcode 7 beta. See below.
Starting with Swift 2 included with the Xcode 7 beta, Function Pointers in Swift work and have been greatly simplified. I turned my example above into this, which works:
import Foundation
func sslReadCallback(connection: SSLConnectionRef,
data: UnsafeMutablePointer<Void>,
var dataLength: UnsafeMutablePointer<Int>) -> OSStatus {
let socketfd = UnsafePointer<Int32>(connection).memory
let bytesRequested = dataLength.memory
let bytesRead = read(socketfd, data, UnsafePointer<Int>(dataLength).memory)
if (bytesRead > 0) {
dataLength = UnsafeMutablePointer<Int>.alloc(1)
dataLength.initialize(bytesRead)
if bytesRequested > bytesRead {
return Int32(errSSLWouldBlock)
} else {
return noErr
}
} else if (bytesRead == 0) {
dataLength = UnsafeMutablePointer<Int>.alloc(1)
dataLength.initialize(0)
return Int32(errSSLClosedGraceful)
} else {
dataLength = UnsafeMutablePointer<Int>.alloc(1)
dataLength.initialize(0)
switch (errno) {
case ENOENT: return Int32(errSSLClosedGraceful)
case EAGAIN: return Int32(errSSLWouldBlock)
case ECONNRESET: return Int32(errSSLClosedAbort)
default: return Int32(errSecIO)
}
}
}
func sslWriteCallback(connection: SSLConnectionRef,
data: UnsafePointer<Void>,
var dataLength: UnsafeMutablePointer<Int>) -> OSStatus {
let socketfd = UnsafePointer<Int32>(connection).memory
let bytesToWrite = dataLength.memory
let bytesWritten = write(socketfd, data, UnsafePointer<Int>(dataLength).memory)
if (bytesWritten > 0) {
dataLength = UnsafeMutablePointer<Int>.alloc(1)
dataLength.initialize(bytesWritten)
if (bytesToWrite > bytesWritten) {
return Int32(errSSLWouldBlock)
} else {
return noErr
}
} else if (bytesWritten == 0) {
dataLength = UnsafeMutablePointer<Int>.alloc(1)
dataLength.initialize(0)
return Int32(errSSLClosedGraceful)
} else {
dataLength = UnsafeMutablePointer<Int>.alloc(1)
dataLength.initialize(0)
if (EAGAIN == errno) {
return Int32(errSSLWouldBlock)
} else {
return Int32(errSecIO)
}
}
}
var socketfd = Darwin.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
var addr = Darwin.sockaddr_in(sin_len: __uint8_t(sizeof(sockaddr_in)), sin_family: sa_family_t(AF_INET), sin_port: CFSwapInt16(8080), sin_addr: in_addr(s_addr: inet_addr("192.168.0.113")), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
var sock_addr = Darwin.sockaddr(sa_len: 0, sa_family: 0, sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
Darwin.memcpy(&sock_addr, &addr, Int(sizeof(sockaddr_in)))
var err = Darwin.connect(socketfd, &sock_addr, socklen_t(sizeof(sockaddr_in)))
if let umc = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType) {
var sslContext = umc.takeRetainedValue()
SSLSetIOFuncs(sslContext, sslReadCallback, sslWriteCallback)
SSLSetConnection(sslContext, &socketfd)
SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnClientAuth, Boolean(1))
SSLHandshake(sslContext)
}
The answer by Hans appears to allocate memory unnecessarily. The following is a Swift 3.1 version with more error checking and URL support and grabs the common name to return (rather than actually reading or writing data).
func getCNforSSL(at url:URL, port:UInt16) -> String? {
var socketfd = Darwin.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
guard let ip = urlToIP(url) else {
NSLog("Could not get IP from URL \(url)")
return nil
}
let inAddr = in_addr(s_addr: inet_addr(ip))
var addr = sockaddr_in(sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size),
sin_family: sa_family_t(AF_INET),
sin_port: CFSwapInt16(port),
sin_addr: inAddr,
sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
var sock_addr = sockaddr(sa_len: 0,
sa_family: 0,
sa_data: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
_ = memcpy(&sock_addr, &addr, MemoryLayout<sockaddr_in>.size)
guard connect(socketfd, &sock_addr, socklen_t(MemoryLayout<sockaddr_in>.size)) == 0 else {
NSLog("Failed connection for \(url) port \(port) with error \(Darwin.errno)")
return nil
}
defer {
if close(socketfd) != 0 {
NSLog("Error closing socket for \(url) port \(port) with error \(Darwin.errno)")
}
}
guard let sslContext = SSLCreateContext(kCFAllocatorDefault, .clientSide, .streamType) else {
NSLog("Could not create SSL Context for \(url) port \(port)")
return nil
}
defer {
SSLClose(sslContext)
}
SSLSetIOFuncs(sslContext, sslReadCallback, sslWriteCallback)
SSLSetConnection(sslContext, &socketfd)
SSLSetSessionOption(sslContext, .breakOnServerAuth, true)
var secTrust:SecTrust? = nil
var status:OSStatus = 0
var subject:String? = nil
repeat {
status = SSLHandshake(sslContext)
if status == errSSLPeerAuthCompleted {
SSLCopyPeerTrust(sslContext, &secTrust)
if let trust = secTrust {
// 0 always garunteed to exist
let cert = SecTrustGetCertificateAtIndex(trust, 0)!
subject = SecCertificateCopySubjectSummary(cert) as String?
}
}
} while status == errSSLWouldBlock
guard status == errSSLPeerAuthCompleted else {
NSLog("SSL Handshake Error for \(url) port \(port) OSStatus \(status)")
return nil
}
return subject
}
func sslReadCallback(connection: SSLConnectionRef,
data: UnsafeMutableRawPointer,
dataLength: UnsafeMutablePointer<Int>) -> OSStatus {
let socketfd = connection.load(as: Int32.self)
let bytesRequested = dataLength.pointee
let bytesRead = read(socketfd, data, UnsafePointer<Int>(dataLength).pointee)
if (bytesRead > 0) {
dataLength.initialize(to: bytesRead)
if bytesRequested > bytesRead {
return Int32(errSSLWouldBlock)
} else {
return noErr
}
} else if (bytesRead == 0) {
dataLength.initialize(to: 0)
return Int32(errSSLClosedGraceful)
} else {
dataLength.initialize(to: 0)
switch (errno) {
case ENOENT: return Int32(errSSLClosedGraceful)
case EAGAIN: return Int32(errSSLWouldBlock)
case ECONNRESET: return Int32(errSSLClosedAbort)
default: return Int32(errSecIO)
}
}
}
func sslWriteCallback(connection: SSLConnectionRef,
data: UnsafeRawPointer,
dataLength: UnsafeMutablePointer<Int>) -> OSStatus {
let socketfd = connection.load(as: Int32.self)
let bytesToWrite = dataLength.pointee
let bytesWritten = write(socketfd, data, UnsafePointer<Int>(dataLength).pointee)
if (bytesWritten > 0) {
dataLength.initialize(to: bytesWritten)
if (bytesToWrite > bytesWritten) {
return Int32(errSSLWouldBlock)
} else {
return noErr
}
} else if (bytesWritten == 0) {
dataLength.initialize(to: 0)
return Int32(errSSLClosedGraceful)
} else {
dataLength.initialize(to: 0)
if (EAGAIN == errno) {
return Int32(errSSLWouldBlock)
} else {
return Int32(errSecIO)
}
}
}
private func urlToIP(_ url:URL) -> String? {
guard let hostname = url.host else {
return nil
}
guard let host = hostname.withCString({gethostbyname($0)}) else {
return nil
}
guard host.pointee.h_length > 0 else {
return nil
}
var addr = in_addr()
memcpy(&addr.s_addr, host.pointee.h_addr_list[0], Int(host.pointee.h_length))
guard let remoteIPAsC = inet_ntoa(addr) else {
return nil
}
return String.init(cString: remoteIPAsC)
}
I asked a network guru peer your question; this was his response:
This person is SOL because Secure Transport requires that you implement C function callbacks and Swift does not currently support that .
I recommend that developer use CFSocketStream, which takes care of TLS and is easily callable from Swift. See the TLSTool sample code.
https://developer.apple.com/library/mac/samplecode/SC1236/