IPv6 Compatibility Issues with Low-Level C API's [closed] - objective-c

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
Apple requires IPv6 Compatibility. Seems a little premature, but I suppose someone has to force it's wide adoption.
I have a client who has some legacy code that will no longer be approved for the App Store because of this incompatibility. I did not write the software by myself, and specifically have never touched this part of the code. I am not much of a ninja when it comes to low-level C networking.
I told the client that part of the problem was that they had an IPv4-only server and had us hardcode an IPv4 address to that server. I updated the IPv4 address to a domain name and told them their servers had to support IPv6. So, they moved them all over, and flipped the switch before any testing could be done. I was notified a few days ago that all their software on the store no longer worked. That's the predicament we are in.
Here is one of potentially many issues that I could use some help on.
Not only does the server that is connected to the port not respond to IPv6, but there are some lower level APIs that are being used that are incompatible.
The first thing I encountered is the use of gethostbyname(). Apparently this is not IPv6 capable. I have been trying to fix it with getaddrinfo() but my sockaddr's are not quite the same.
The second issue I can see is that apparently I need to be using AF_UNSPEC instead of AF_INET. So I am attempting to open a socket in the following way:
int sockfd = socket(AF_UNSPEC, SOCK_STREAM, 0);
I always get -1.
If I open a sock using AF_INET I get further, but then I have this issues:
otherAddr sockaddr * 0x618000220680 0x0000618000220680
sa_len __uint8_t '\x1c'
sa_family sa_family_t '\x1e'
sa_data char [14] "\x13\x88"
otherAddrCast sockaddr * 0x7000052d3c28 0x00007000052d3c28
sa_len __uint8_t '\0'
sa_family sa_family_t '\0'
sa_data char [14] "\x13\x88\"Ԛ\\"
So some of the sa_data is the same. and if I don't cast the sockaddr_in to a struct sockaddr* then the sa_data is the same.
Problem is I don't know what most of this means. Help?
EDIT: Here is my code. Assume that I am only getting 1 addr back (because I am)
struct hostent* server = gethostbyname([_hostname UTF8String]);
struct addrinfo *ai;
struct addrinfo hints;
memset(&hints, 0x00, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
NSString* portString = [NSString stringWithFormat:#"%d", _port];
getaddrinfo([_hostname UTF8String], [portString cStringUsingEncoding:kCFStringEncodingASCII] , &hints, &ai);
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
bcopy(server->h_addr, &addr.sin_addr.s_addr, server->h_length);
addr.sin_port = htons(_port);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
_addr = (struct sockaddr*)&addr;
int status = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
int status2 = connect(socket(ai->ai_family,ai->ai_socktype,0), ai->ai_addr, ai->ai_addrlen);

You are NOT supposed to use AF_UNSPEC with socket() at all. You MUST use either AF_INET (IPv4) or AF_INET6 (IPv6).
You can use AF_UNSPEC with the hints input parameter of getaddrinfo() to indicate that you are willing to accept both IPv4 and IPv6 addresses as output. The actual addresses in the output will be either AF_INET or AF_INET6. Or, you can set the hints to AF_INET for IPv4-only output. Or AF_INET6 for IPv6-only output.
You are supposed to loop through the list that is returned by getaddrinfo(). For each address in the list:
pass its ai_family, ai_socktype, and ai_protocol fields to socket()
then pass its ai_addr and ai_addrlen fields to bind() (servers) or connect() (clients).
Repeat this for all addresses reported for a listening server, and for all addresses reported for a client until one successfully connects.
This way, each socket you create matches the IP version of the address it is working with, and you are passing an appropriate matching sockaddr_in (IPv4) or sockaddr_in6 (IPv6) to bind()/connect().
Once you have successful listening server socket(s), or a successfully connected client socket, if you need to retrieve an IP from a socket using accept(), getsockname() or getpeername(), be sure to pass it a sockaddr_storage struct to fill in. sockaddr_storage is large enough to hold all defined sockaddr-based structures (and there are many). If successful, you can then type-cast it to a sockaddr_in or sockaddr_in6 based on its sa_family field (AF_INET or AF_INET6, respectively). Same goes for other similar functions, like inet_pton() and inet_ntop().
Update: given the client code you have shown, try this instead:
struct addrinfo hints = {0};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
NSString* portString = [NSString stringWithFormat:#"%d", _port];
struct addrinfo *ai;
int sockfd = -1;
int status = getaddrinfo([_hostname UTF8String], [portString cStringUsingEncoding:kCFStringEncodingASCII] , &hints, &ai);
if (status == 0) {
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd == -1) {
status = errno;
}
else if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
status = errno;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(ai);
}
if (sockfd == -1) {
// handle status error as needed...
}
else {
// use sockfd as needed...
close(sockfd);
}

Related

Creating and binding socket on Mac OS Hight Sierra

I have serious and strange problem with creating socket in my application for Hight Sierra. If I create command Line tool, everything is ok! I create socket, bind socket. But If I trying to create Cocoa App, I can't binding my socket! :(
If I use CFSockets in Cocoa App,
char punchline[] = "MESSAGE from Server!";
int yes = 1;
CFSocketContext CTX = {0, punchline, NULL, NULL, NULL};
CFSocketRef TCPServer = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack) &AcceptCallBack, &CTX);
if (TCPServer == NULL) return;
setsockopt(CFSocketGetNative(TCPServer), SOL_SOCKET, SO_REUSEADDR, (void *) &yes, sizeof(yes));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(33000);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *address = [NSData dataWithBytes:&addr length:sizeof(addr)];
if (CFSocketSetAddress(TCPServer, (CFDataRef) address) != kCFSocketSuccess) {
CFRelease(TCPServer);
return;
}
I get this message:
CFSocketSetAddress bind failure: 1
If I use low level C function in Cocoa App, like this:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0) error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
...it's not work too, and I get error on binding and also this message in console:
ERROR: Operation not permitted
But in command line tool everything is working well!
What kind of project settings (may be in info.plist) I need to fix?
Where is a trouble? :(
Help me! :(((
The problem was that the application was sandboxed and did not have the Network: Incoming Connections entitlement. That entitlement can be added in Xcode under the App Sandbox details in the Capabilities tab of the target settings.
To Fix it in macOS Catalina Version 10.15.3:

Get description of network adapter using Swift [duplicate]

I am trying to get a list of active network interfaces with end user understandable names. Like the names listed in System Preferences instead of en0 en5.
I have the raw interfaces using getifaddrs but haven't been able to find how to take those and get the system names of Ethernet or Wifi.
Anyone know how to do this? This would be for macOS.
What I have now:
struct ifaddrs *ifap;
if( getifaddrs(&ifap) == 0 ){
struct ifaddrs *interface;
for (interface = ifap; interface != NULL; interface = interface->ifa_next) {
unsigned int flags = interface->ifa_flags;
struct sockaddr *addr = interface->ifa_addr;
// 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 == AF_INET || addr->sa_family == AF_INET6) {
// Convert interface address to a human readable string:
char host[NI_MAXHOST];
getnameinfo(addr, addr->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
printf("interface:%s, address:%s\n", interface->ifa_name, host);
// MAGIC HERE TO CONVERT ifa_name to "Ethernet" or something
}
}
}
freeifaddrs(ifap);
This is possible with System Configuration on macOS. In Objective-C like so:
CFArrayRef ref = SCNetworkInterfaceCopyAll();
NSArray* networkInterfaces = (__bridge NSArray *)(ref);
for(int i = 0; i < networkInterfaces.count; i += 1) {
SCNetworkInterfaceRef interface = (__bridge SCNetworkInterfaceRef)(networkInterfaces[i]);
CFStringRef displayName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
CFStringRef bsdName = SCNetworkInterfaceGetBSDName(interface);
NSLog(#"Name:%# \ninterface: %#\nbsd:%#",displayName, SCNetworkInterfaceGetInterfaceType(interface), bsdName);
}
The localized display name will be something like Display Ethernet or WiFi and the BSD name will be something like en5 which will allow matching to the above code.
This approach doesn't work on iOS, but there aren't really any other configurations on iOS anyway.

Broken Pipe signal on a DNS client

I'm building a DNS client. A child process handles the request through an UDP socket, while the parent handles the reply. I need the parent to know how many bytes were sent, in order to print the URLs. I tried the following approach with pipe()
childPID = fork();
pipe(fd);
if(childPID == 0){
close(fd[0]);
sent_bytes = sendDNS(sock_udp, &serverAddr, argv[2]);
memcpy(in_buf, &sent_bytes, sizeof(sent_bytes));
write(fd[1], in_buf, sizeof(sent_bytes));
exit(0);
}
else{
close(fd[1]);
int inBytes = -1;
struct sockaddr reply_addr;
n = sizeof(reply_addr);
while(inBytes < 0){
inBytes = recvfrom(sock_udp, buffer, DNS_MAX_RESPONSE, 0, &reply_addr, (socklen_t*)&n);
read(fd[0], out_buf, sizeof(sent_bytes));
memcpy(pipe_msg, out_buf, sizeof(sent_bytes));
printDNSmsg((struct dnsReply*)buffer);
}
}
But GDB shows a SIGPIPE received on the child process. What am I missing?
How would you print a DNS reply (variable length buffer)?
You need to call pipe() before fork(), of course. But you're not actually using the information anywhere. Why do you care how many bytes were sent, as long as you got a reply? And why would you do a UDP send in a separate thread, let alone a separate process? It all seems completely pointless.

bind() error : Cannot assign requested address

bind () error : Cannot assign requested address.
new_socket= socket(AF_INET, SOCK_DGRAM, 0);
localIP = "128.1.1.64";
memset(&socket_data, 0, sizeof(socket_data));
// Fill the socket structure
socket_data.sin_family = AF_INET;
socket_data.sin_addr.s_addr = inet_addr(localIP);
socket_data.sin_port = htons(PortNumber);
bind( new_socket, (struct sockaddr*) &socket_data, sizeof(socket_data))
Does any one know why the bind() is failing?
I guess 128.1.1.64 is an arbitrary IP which does not exist in any of your net interface. If you want to spoof the source IP, probably you need to use RAW_SOCKET.
You have a socket already bound to that address/port combination is my guess

winsock - Sender UDP socket not bound to the desired port

Below you can see my code that implements a pretty basic UDP sender in C++ with Winsock. The thing is that no matter how many times I run the code, the socket (the listenSocket) gets bound to a different UDP port. Is there any specific reason for this? Am I doing some mistake in my code?
thanks
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
using namespace std;
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKADDR_IN myAddress;
SOCKADDR_IN targetAddress;
int myPort = 60888;
const char *myIP = "192.168.0.1";
int remotePort = 2048;
const char *remoteIP = "192.168.0.2";
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET SendSocket = INVALID_SOCKET;
SOCKET acceptSocket;
char cBuffer[1024] = "Test Buffer";
int nBytesSent = 0;
int nBufSize = strlen(cBuffer);
int iResult;
// Initialize Winsock
if( WSAStartup( MAKEWORD(2, 2), &wsaData ) != NO_ERROR )
{
cerr<<"Socket Initialization: Error with WSAStartup\n";
system("pause");
WSACleanup();
exit(10);
}
ListenSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
SendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ListenSocket == INVALID_SOCKET or SendSocket == INVALID_SOCKET)
{
cerr<<"Socket Initialization: Error creating socket"<<endl;
system("pause");
WSACleanup();
exit(11);
}
//bind
myAddress.sin_family = AF_INET;
myAddress.sin_addr.s_addr = inet_addr(myIP);
myAddress.sin_port = htons(myPort);
targetAddress.sin_family = AF_INET;
targetAddress.sin_addr.s_addr = inet_addr(remoteIP);
targetAddress.sin_port = htons(remotePort);
if(bind(ListenSocket, (SOCKADDR*) &myAddress, sizeof(myAddress)) == SOCKET_ERROR)
{
cerr<<"ServerSocket: Failed to connect\n";
system("pause");
WSACleanup();
exit(14);
}
else
printf("Server: bind() is OK.\n");
nBytesSent = sendto(SendSocket, cBuffer, nBufSize, 0,
(SOCKADDR *) &targetAddress,
sizeof(SOCKADDR_IN));
printf("Everything is ok\n");
system("PAUSE");
closesocket(ListenSocket);
closesocket(SendSocket);
return EXIT_SUCCESS;
}
EDIT: Maybe I was not so clear. What I do with this code is to send some data to a remote PC. But what is required is that the UDP segments should appear to be originated from a specific port. How can this be done? Is it wrong what I'm doing here? Now that I'm thinking of it, I guess it is wrong indeed. The SendSocket and ListenSocket don't have any connection, correct? So, how can I make it that the UDP segments appear to originate from a specific UDP port? Thanks!
You are not calling bind() on SendSocket before sending data with it, so WinSock is free to bind that socket to whatever random local IP/Port it needs to. If you have to send data with a specific source IP/Port every time, you have to bind() to that IP/Port first. If that local IP/Port is the same pair you are binding ListenSocket to, then you don't need to use two separate sockets to begin with. You can send data with the same socket that is listening for incoming data.