Getting an Ip Address and Port Number from a Sockaddr_In Struct in Swift

Getting an IP address and port number from a sockaddr_in struct in Swift?

If you need the IP address and port as numbers then you can
access the corresponding fields of a sockaddr_in directly, but
remember to convert the values from network byte (big endian) to host
byte order:

let saddr: sockAddr = ...

let port = in_port_t(bigEndian: sockAddr.sin_port)
let addr = in_addr_t(bigEndian: sockAddr.sin_addr.s_addr)

getnameinfo() can be used to extract the IP address as a string
(in dotted-decimal notation), and optionally the port as well.
Casting a struct sockaddr_in pointer to a struct sockaddr pointer
is called "rebinding" in Swift, and done with withMemoryRebound():

var sockAddr: sockaddr_in = ...

var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
var service = [CChar](repeating: 0, count: Int(NI_MAXSERV))

withUnsafePointer(to: &sockAddr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 0) {
_ = getnameinfo($0, socklen_t($0.pointee.sa_len),
&hostname, socklen_t(hostname.count),
&service, socklen_t(service.count),
NI_NUMERICHOST | NI_NUMERICSERV)
}
}
print("addr:", hostname)
print("port:", service)

This works for both IPv4 and IPv6 socket address structures (sockaddr_in and sockaddr_in6).

For more information about "unsafe pointer conversions", see
SE-0107 UnsafeRawPointer API
and UnsafeRawPointer Migration. The latter page contains example code how to handle
socket addresses in Swift 3.

Getting IPV4 address from a sockaddr structure

Once sockaddr cast to sockaddr_in, it becomes this:

struct sockaddr_in {
u_short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};

Swift obtaining ip address from socket returns weird value

The main error is that inet_ntop() takes the address of a struct in_addr
(or struct in_addr6 for IPv6), and not the address of a struct sockaddr.

Another error is that the length argument to getpeername() must be the length of
the passed socket address structure, you are passing the length of an Int32.

Your current code passes AF_INET to inet_ntop() and is therefore limited
to IPv4 addresses. If that is sufficient for you, the following should work:

var addr = UnsafeMutablePointer<sockaddr_in>.alloc(1)
var len = socklen_t(sizeofValue(addr.memory))

if getpeername(sockfd, UnsafeMutablePointer(addr), &len) != -1 {
var ipAddressString = [CChar](count:Int(INET_ADDRSTRLEN), repeatedValue: 0)
inet_ntop(
AF_INET ,
&addr.memory.sin_addr, // <-- main difference here
&ipAddressString,
socklen_t(INET_ADDRSTRLEN))
println("socket \(sockfd) ip \(String.fromCString(ipAddressString)!)")
}
addr.dealloc(1)

Allocating the socket address structure makes casting between the different pointer
types a bit easier. I have also replaced the variable name socket by sockfd to
avoid confusion with the socket() function.

The more "modern" function to convert socket addresses to strings is
getnameinfo(). The following code demonstrates how to use it. It works for
both IPv4 and IPv6 addresses:

var addr = UnsafeMutablePointer<sockaddr_storage>.alloc(1)
var len = socklen_t(sizeofValue(addr.memory))

if getpeername(sockfd, UnsafeMutablePointer(addr), &len) != -1 {

var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
if (getnameinfo(UnsafeMutablePointer(addr), socklen_t(addr.memory.ss_len),
&hostBuffer, socklen_t(hostBuffer.count), nil, 0,
NI_NUMERICHOST) == 0) {
let host = String.fromCString(hostBuffer)!
println("socket \(sockfd) ip \(host)")
}
}
addr.dealloc(1)

Swift 2 update: First method:

var addr = sockaddr_in()
var len = socklen_t(sizeofValue(addr))

withUnsafeMutablePointer(&addr) {
if getpeername(sockfd, UnsafeMutablePointer($0), &len) != -1 {
var ipAddressString = [CChar](count:Int(INET_ADDRSTRLEN), repeatedValue: 0)
inet_ntop(
AF_INET ,
&addr.sin_addr, // <-- main difference here
&ipAddressString,
socklen_t(INET_ADDRSTRLEN))
print("socket \(sockfd) ip \(String.fromCString(ipAddressString)!)")
}
}

Second method:

var addr = sockaddr_storage()
var len = socklen_t(sizeofValue(addr))

withUnsafeMutablePointer(&addr) {
if getpeername(sockfd, UnsafeMutablePointer($0), &len) != -1 {
var hostBuffer = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
if (getnameinfo(UnsafeMutablePointer($0), socklen_t(addr.ss_len),
&hostBuffer, socklen_t(hostBuffer.count), nil, 0,
NI_NUMERICHOST) == 0) {
let host = String.fromCString(hostBuffer)!
print("socket \(sockfd) ip \(host)")
}
}
}

How to create ip address and port into NSData to work with

From the header file:

 * Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object.
*
* If you have an existing struct sockaddr you can convert it to a NSData object like so:
* struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
* struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];

So, you need a sockaddr:

#include <netinet/in.h>
#include <arpa/inet.h>

struct sockaddr_in ip;
ip.sin_family = AF_INET;
ip.sin_port = htons(6003);
inet_pton(AF_INET, "0.0.0.0", &ip.sin_addr);

NSData * discoveryHost = [NSData dataWithBytes:&ip length:ip.sin_len];

Here's some documentation on sockaddr - http://www.beej.us/guide/bgnet/output/html/multipage/sockaddr_inman.html

Unable to copy ip address from string to sockaddr_in

This is the problem in your pastebin code:

ip[strlen(ip)]='\0';

(Trying to append null terminator but using strlen, which itself depends on a null terminator to be present).

Here is the fix:

....
while(buf[i]!=':')
i++;
strncpy(ip,&buf[0],i);
ip[i]='\0';
puts(ip);
....

How to get sockaddr from sockaddr_in in Swift?

I translated the code based on your start as:

func send(_ address: String) {
let socketSD = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)
guard socketSD > 0 else { print("Error: Could not open socket."); return }
defer { close(socketSD) }

// set socket options enable broadcast
var broadcastEnable = 1
let ret = setsockopt(socketSD, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, socklen_t(MemoryLayout.size(ofValue: broadcastEnable)))
guard ret == 0 else { print("Error: Could not open set socket to broadcast mode"); return }

var broadcastAddr = sockaddr_in(sin_len: 0,
sin_family: sa_family_t(AF_INET),
sin_port: in_port_t(UInt16(1314).bigEndian),
sin_addr: in_addr(s_addr: 0),
sin_zero: (0,0,0,0,0,0,0,0))
let broadcastAddrSize = MemoryLayout.size(ofValue: broadcastAddr)

address.withCString {
address in
inet_pton(AF_INET, address, &broadcastAddr.sin_addr)
}

let result = withUnsafePointer(to: &broadcastAddr) {
return $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { aSockaddr in
return "message".withCString { message in
return sendto(socketSD, message, strlen(message), 0, aSockaddr, socklen_t(broadcastAddrSize))

}
}
}

print(result)
if result < 0 {
print("Error: Could not open send broadcast. \(result)")
}
}

If I run it, it doesn't crash and returns a positive result, but I don't know that it works properly.

How to get Ip address in swift

As it turned out in the discussion, OP needs the interface address on a Mac and not on an iOS device as I thought initially. The code referenced in the question checks for the
interface name "en0", which is the WiFi interface on the iPhone. On a Mac it makes more
sense to check for any "up-and-running" interface instead.
Therefore I have rewritten the answer. It is now a Swift translation of the code in
Detect any connected network.


getifaddrs() is defined in <ifaddrs.h>, which is not included by default.
Therefore you have to create a bridging header and add

#include <ifaddrs.h>

The following function returns
an array with the names of all local "up-and-running" network interfaces.

func getIFAddresses() -> [String] {
var addresses = [String]()

// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
if getifaddrs(&ifaddr) == 0 {

// For each interface ...
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr.memory.ifa_next }

let flags = Int32(ptr.memory.ifa_flags)
let addr = ptr.memory.ifa_addr.memory

// 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](count: Int(NI_MAXHOST), repeatedValue: 0)
if (getnameinfo(ptr.memory.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
if let address = String.fromCString(hostname) {
addresses.append(address)
}
}
}
}
}
freeifaddrs(ifaddr)
}

return addresses
}

Update for Swift 3: In addition to adopting the code to the
many changes in Swift 3,
iterating over all interfaces can now use the new generalized
sequence() function:

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)
let 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(ptr.pointee.ifa_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
}

ReachabilityCreateWithAddress struct in_addr, ip-format

You can use inet_pton() (defined in <arpa/inet.h>) to convert a string containing
a network address to network format. And note that you have to convert the port
number from host byte order to network byte order:

struct sockaddr_in ipAddress;
ipAddress.sin_len = sizeof(ipAddress);
ipAddress.sin_family = AF_INET;
ipAddress.sin_port = htons(80);
inet_pton(AF_INET, "192.168.2.2", &ipAddress.sin_addr);


Related Topics



Leave a reply



Submit