How to find a free local port using Swift? - swift

For a local server I need to specify a port, which must not be in use. There's a really neat solution in Python to get a free port. However, such a socket library is not available in Swift. So I tried Using BSD Sockets in Swift, but that actually wants a port to be specified upfront and I cannot get the bind command to work. Here's the code I tried:
let socketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if socketFD == -1 {
print("Error creating BSD Socket")
return
}
var hints = addrinfo(
ai_flags: AI_PASSIVE, // Assign the address of the local host to the socket structures
ai_family: AF_UNSPEC, // Either IPv4 or IPv6
ai_socktype: SOCK_STREAM, // TCP
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil)
var servinfo: UnsafeMutablePointer<addrinfo>? = nil
let addrInfoResult = getaddrinfo(
nil, // Any interface
"8000", // The port on which will be listenend
&hints, // Protocol configuration as per above
&servinfo);
if addrInfoResult != 0 {
print("Error getting address info: \(errno)")
return
}
let bindResult = Darwin.bind(socketFD, servinfo!.pointee.ai_addr, socklen_t(servinfo!.pointee.ai_addrlen));
if bindResult == -1 {
print("Error binding socket to Address: \(errno)")
return
}
let listenResult = Darwin.listen(socketFD, 1);
if listenResult == -1 {
print("Error setting our socket to listen")
return
}
let port = Darwin.getsockname(socketFD, nil, nil);
The bind call always returns -1 and since I want to get a free port it makes no sense to specify one in getaddrinfo. What's the correct way here?

Here's a working solution:
func findFreePort() -> UInt16 {
var port: UInt16 = 8000;
let socketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if socketFD == -1 {
//print("Error creating socket: \(errno)")
return port;
}
var hints = addrinfo(
ai_flags: AI_PASSIVE,
ai_family: AF_INET,
ai_socktype: SOCK_STREAM,
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil
);
var addressInfo: UnsafeMutablePointer<addrinfo>? = nil;
var result = getaddrinfo(nil, "0", &hints, &addressInfo);
if result != 0 {
//print("Error getting address info: \(errno)")
close(socketFD);
return port;
}
result = Darwin.bind(socketFD, addressInfo!.pointee.ai_addr, socklen_t(addressInfo!.pointee.ai_addrlen));
if result == -1 {
//print("Error binding socket to an address: \(errno)")
close(socketFD);
return port;
}
result = Darwin.listen(socketFD, 1);
if result == -1 {
//print("Error setting socket to listen: \(errno)")
close(socketFD);
return port;
}
var addr_in = sockaddr_in();
addr_in.sin_len = UInt8(MemoryLayout.size(ofValue: addr_in));
addr_in.sin_family = sa_family_t(AF_INET);
var len = socklen_t(addr_in.sin_len);
result = withUnsafeMutablePointer(to: &addr_in, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
return Darwin.getsockname(socketFD, $0, &len);
}
});
if result == 0 {
port = addr_in.sin_port;
}
Darwin.shutdown(socketFD, SHUT_RDWR);
close(socketFD);
return port;
}

Related

CFSocket in Swift (4)

I try to establish a connection to a TCP server, unfortunately without success. Here is my actual way. Does anyone knows, where the error is? I'm getting the error at CFSocketConnectToAddress (I get the .error result, so my code results in the error while connecting print).
Any ideas?
host = "192.168.0.20"
port = 8888
socket = CFSocketCreate(kCFAllocatorDefault,
AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
CFSocketCallBackType.readCallBack.rawValue, { (socket: CFSocket?, callBackType: CFSocketCallBackType, address: CFData?, data: UnsafeRawPointer?, info: UnsafeMutableRawPointer?) -> Void in print("callback test") }, nil)
if socket == nil {
print("Error while creating socket")
} else {
var sin = sockaddr_in(
sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
sin_family: sa_family_t(AF_INET),
sin_port: in_port_t(port),
sin_addr: in_addr(s_addr: inet_addr(host)),
sin_zero: (0,0,0,0,0,0,0,0)
)
addressData = NSData(bytes: &sin, length: MemoryLayout.size(ofValue: sin)) as CFData
let connectResult: CFSocketError = CFSocketConnectToAddress(socket, addressData!, 10)
switch connectResult {
case .success:
print("Connected")
case .error:
print("Error while connecting")
case .timeout:
print("Timeout while connecting")
}
The problem was the port! The htons macro is not available in swift. This is the working way now:
func connectToServer(timeout: Int=10) throws {
// check, if address is valid https://linux.die.net/man/3/inet_makeaddr
let inAddr = inet_addr(host)
if inAddr == INADDR_NONE {
throw SocketError.noValidAddress
}
let socket = CFSocketCreate(kCFAllocatorDefault,
AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
CFSocketCallBackType.readCallBack.rawValue,
{ (socket, callBackType, address, data, info) in
TCPClientCallBack(socket: socket, callBackType: callBackType, address: address, data: data, info: info)
},
nil)
if socket == nil {
throw SocketError.socketCreationFailed
}
var sin = sockaddr_in() // https://linux.die.net/man/7/ip
sin.sin_len = __uint8_t(MemoryLayout.size(ofValue: sin))
sin.sin_family = sa_family_t(AF_INET)
sin.sin_port = UInt16(port).bigEndian
sin.sin_addr.s_addr = inAddr
let addressDataCF = NSData(bytes: &sin, length: MemoryLayout.size(ofValue: sin)) as CFData
let socketErr = CFSocketConnectToAddress(socket, addressDataCF, CFTimeInterval(timeout))
switch socketErr {
case .success:
print("connected")
case .error:
throw SocketError.connectionError
case .timeout:
throw SocketError.connectionTimeout
}
}
enum SocketError: Error {
case noValidAddress
case socketCreationFailed
case connectionError
case connectionTimeout
}

Get NetworkIp in Swift 3

Any one help me to get Network IP (i.e 103.62.238.190) in Swift 3
I tried below code for it, but this function get System Ip.
I tried so many time to find the solution but still i didn't get any exact function for it.
func getIFAddresses() -> [String] {
var addresses = [String]()
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return [] }
guard let firstAddr = ifaddr else { return [] }
// For each interface ...
for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let flags = Int32(ptr.pointee.ifa_flags)
var addr = ptr.pointee.ifa_addr.pointee
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
addresses.append(address)
}
}
}
}
freeifaddrs(ifaddr)
return addresses
}

Swift - server socket cannot rebind

I am trying to program a simple server socket. It is successful to connect to client. But after I disconnect, and try to re-establish the server socket, during binding I got an error (errno = 48, EADDRINUSE). Anyone know how to fix it?
func initServerSocket(serverSocket: Int32) -> Int32 {
var clientSocket: Int32 = -1
var server_addr_size: Int
var client_addr_size = socklen_t(MemoryLayout<sockaddr_in>.size)
var server_addr = sockaddr_in()
var client_addr = sockaddr_in()
server_addr_size = MemoryLayout<sockaddr_in>.size
memset(&server_addr, 0, server_addr_size);
server_addr.sin_family = sa_family_t(AF_INET)
server_addr.sin_port = UInt16(8080).bigEndian
server_addr.sin_addr.s_addr = UInt32(0x00000000)
let bind_server = withUnsafePointer(to: &server_addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
bind(serverSocket, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
if bind_server == -1 {
print("[Fail] Bind Port, Error: \(errno)");
exit(1);
} else {
print("[Success] Binded Port \(bind_server)");
}
if listen(serverSocket, 5) == -1 {
print("[Fail] Listen");
exit(1);
} else {
print("[Success] Listening : \(server_addr.sin_port) Port ...");
}
clientSocket = withUnsafeMutablePointer(to: &client_addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
accept(serverSocket, $0, &client_addr_size)
}
}
if clientSocket == -1 {
print("[Fail] Accept Client Connection \(errno)");
exit(1);
} else {
print("[Success] Accepted Client : \(inet_ntoa(client_addr.sin_addr)!) : \(client_addr.sin_port)!");
}
return clientSocket
}
Disconnect function:
func disconnect() {
if clientSocket != -1 {
self.inputStream.close()
self.outputStream.close()
if close(HiveConfig.clientSocket) == -1 {
print("serverSocket close error")
}
if close(HiveConfig.serverSocket) == -1 {
print("serverSocket close error")
}
}
}
I figure out that on my code before bind need to add the following code to make the address reused.
var opt:Int = 1
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, socklen_t(MemoryLayout.size(ofValue: opt)));

Get the currently active network adapter in Swift

I am using the following code to get the network adapters of my macOS System:
private func getAdapters() -> [Adapter]
{
var adapters: [Adapter] = []
var addresses: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&addresses) == 0 else { return adapters }
guard let firstAddr = addresses else { return adapters }
for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next })
{
let address = ptr.pointee
let rawData = address.ifa_data
let name = address.ifa_name
let socket: sockaddr = address.ifa_addr.pointee
// Set up some filters
let loopback = (address.ifa_flags & UInt32(IFF_LOOPBACK)) != 0
let up = (address.ifa_flags & UInt32(IFF_UP)) != 0
let p2p = (address.ifa_flags & UInt32(IFF_POINTOPOINT)) != 0
if rawData != nil && name != nil && socket.sa_family == UInt8(AF_LINK) && !loopback && up && !p2p
{
let adapterName = String(utf8String: UnsafePointer<CChar>(name!))
let adapter = Adapter(name: adapterName!)
adapters.append(adapter)
}
}
freeifaddrs(addresses)
return adapters
}
Now I am looking for a way to figure out, which of those adapters is the "active" one, i.e. which one is connected to the internet.
I want to get the adapter that has the "green dot" in the network settings. How can I get this information?
Regards,
Sascha

How to use swift convert string to ipv6 addr

I have a ipv6 string,how to use Swift convert to in6_addr
let ipString = "2001:0b28:f23f:f005:0000:0000:0000:000a"
You can use inet_pton()
similarly as you would in a C program:
let ipString = "2001:0b28:f23f:f005:0000:0000:0000:000a"
var addr = in6_addr()
let retval = withUnsafeMutablePointer(&addr) {
inet_pton(AF_INET6, ipString, UnsafeMutablePointer($0))
}
if retval == 0 {
print("Invalid address")
} else if retval == -1 {
print("Failed:", String.fromCString(strerror(errno)) ?? "\(errno)")
// For Swift 3, replace the last line by
// print("Failed:", String(cString: strerror(errno)))
} else {
// Success, `addr` contains the result.
}
withUnsafeMutablePointer() is necessary to pass the address of
the addr variable to inet_pton().
Swift 5 & Network
You can use IPv6Address from Network to work with IPv6 addresses. It is useful for working with local or remote endpoints NWEndpoints, for validating string IPv6 addresses, shortening etc.
There is a extension of IPv6Address that implements helper functions for in6_addr:
import Network
extension IPv6Address {
var address: in6_addr {
rawValue.withUnsafeBytes {
$0.load(as: in6_addr.self)
}
}
var stringValue: String { debugDescription }
init(_ address: in6_addr) {
var addr = address
let data = withUnsafeBytes(of: &addr) { Data($0) }
self.init(data)!
}
}
How to use:
let ipString = "2001:0b28:f23f:f005:0000:0000:0000:000a"
// Create IPv6Address from string
if let ipv6 = IPv6Address(ipString) {
// Get in6_addr from IPv6Address
let addr = ipv6.address
print(addr) // in6_addr(__u6_addr: __C.in6_addr.__Unnamed_union___u6_addr())
// Create IPv6Address from in6_addr
let ipv6 = IPv6Address(addr)
print(ipv6.stringValue) // 2001:b28:f23f:f005::a
}