IOS Get Proxy Settings - ios

In my project i am using libcurl to download data over internet. The problem is that libcurl doesn't detect the proxy settings of the wifi connection.
I must set manually the settings for libcurl so i'm wondering how can a get the proxy settings of a wifi connection. I found some clues about informations in the KeyChain but i was unable to retrieve them.
Do you know if there is a way to get this settings so i can set them for libcurl ?
Thanks !

I found the response !
Using this bit of code seems to work :
std::string getProxyName()
{
CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings();
const CFStringRef proxyCFstr = (const CFStringRef)CFDictionaryGetValue(dicRef, (const void*)kCFNetworkProxiesHTTPProxy);
char buffer[4096];
memset(buffer, 0, 4096);
if (CFStringGetCString(proxyCFstr, buffer, 4096, kCFStringEncodingUTF8))
{
return std::string(buffer);
}
return "";
}
int CDownloadThread::getProxyPort()
{
CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings();
const CFNumberRef portCFnum = (const CFNumberRef)CFDictionaryGetValue(dicRef, (const void*)kCFNetworkProxiesHTTPPort);
SInt32 port;
if (CFNumberGetValue(portCFnum, kCFNumberSInt32Type, &port))
{
return port;
}
return -1;
}
I haven't try with automatic proxy configuration yet but i hope it's working !

It will give IP address as String.
(NSString *)proxyName
{
CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings();
const CFStringRef proxyCFstr = (const CFStringRef)CFDictionaryGetValue(dicRef,
(const void*)kCFNetworkProxiesHTTPProxy);
return (__bridge NSString *)proxyCFstr;
}

Related

iOS: How to specify DNS to be used to resolve hostname to IP address?

As the title says I have hostname (eg www.example.com) that I want to resolve using specified DNS server. For example in one case I want to use google's IPv4 DNS and in other case google's IPv6 DNS.
I have browsed SO for something like this on iOS, and found questions like this one (Swift - Get device's IP Address), so I am sure it can be done, but I am unclear how?
How can I do this?
EDIT 06/07/2018
#mdeora suggested solution from http://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/
This solution works but only if I use IPv4 DNS, for example google's "8.8.8.8". If I try to use IPv6 DNS 2001:4860:4860::8888, i get nothing.
I have managed to change conversion:
void setup_dns_server(res_state res, const char *dns_server)
{
res_ninit(res);
struct in_addr addr;
// int returnValue = inet_aton(dns_server, &addr);
inet_pton(AF_INET6, dns_server, &addr); // for IPv6 conversion
res->nsaddr_list[0].sin_addr = addr;
res->nsaddr_list[0].sin_family = AF_INET;
res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
res->nscount = 1;
};
But still have trouble with this:
void query_ip(res_state res, const char *host, char ip[])
{
u_char answer[NS_PACKETSZ];//NS_IN6ADDRSZ NS_PACKETSZ
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
if(ns_msg_count(handle, ns_s_an) > 0) {
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
strcpy(ip, inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
}
I get -1 for len. From what I gather it seems I need to configure res_state for IPv6.
Here the code from my blogpost, that was already mentioned above, just slightly adapted to use IPv6.
Adapt setup_dns_server
First we could start with the changes to setup_dns_server:
void setup_dns_server(res_state res, const char *dns_server) {
struct in6_addr addr;
inet_pton(AF_INET6, dns_server, &addr);
res->_u._ext.ext->nsaddrs[0].sin6.sin6_addr = addr;
res->_u._ext.ext->nsaddrs[0].sin6.sin6_family = AF_INET6;
res->_u._ext.ext->nsaddrs[0].sin6.sin6_port = htons(NS_DEFAULTPORT);
res->nscount = 1;
}
Add __res_state_ext
This wouldn't compile because of a missing struct __res_state_ext. This structure is unfortunately in a private header file.
But the definition of that one can be take from here:
https://opensource.apple.com/source/libresolv/libresolv-65/res_private.h.auto.html :
struct __res_state_ext {
union res_sockaddr_union nsaddrs[MAXNS];
struct sort_list {
int af;
union {
struct in_addr ina;
struct in6_addr in6a;
} addr, mask;
} sort_list[MAXRESOLVSORT];
char nsuffix[64];
char bsuffix[64];
char nsuffix2[64];
};
The struct can be added e.g. at the top of the file.
Adapt resolveHost
The changes here include the longer buffer for ip (INET6_ADDRSTRLEN). res_ninit moved from setup_dns_server into this method and is matched now with a res_ndestroy.
+ (NSString *)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer {
struct __res_state res;
char ip[INET6_ADDRSTRLEN];
memset(ip, '\0', sizeof(ip));
res_ninit(&res);
setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
query_ip(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], ip);
res_ndestroy(&res);
return [[NSString alloc] initWithCString:ip encoding:NSASCIIStringEncoding];
}
Retrieving IPv6 addresses
The changes above are already sufficient if you just want to use a IPv6 address for your DNS server. So in query_ip there are no changes necessary if you still want to retrieve the IPv4 addresses.
In case you would like to retrieve IPv6 addresses from the DNS server also, you can do this:
void query_ip(res_state res, const char *host, char ip[]) {
u_char answer[NS_PACKETSZ];
int len = res_nquery(res, host, ns_c_in, ns_t_aaaa, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
if(ns_msg_count(handle, ns_s_an) > 0) {
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
inet_ntop(AF_INET6, ns_rr_rdata(rr), ip, INET6_ADDRSTRLEN);
}
}
}
Please note: we use here ns_t_aaaa to get AAAA resource records (quad-A record), because in DNS this specifies the mapping between IPv6 address and hostname. For many hosts, there is no such quad-A record, meaning you can just reach them via IPv4.
Call
You would call it e.g. like so:
NSString *resolved = [ResolveUtil resolveHost:#"www.google.com" usingDNSServer:#"2001:4860:4860::8888"];
NSLog(#"%#", resolved);
The result would the look like this:
Disclaimer
These are just simple example calls, that demonstrate the basic usage of the functions. There is no error handling.
You can do this using below swift code -
import Foundation
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = ["dig", "#8.8.8.8", "google.com"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print(output!)
In the above code use the DNS server of your choice by replacing 8.8.8.8
For Objective-C iOS refer below link -
https://www.software7.com/blog/programmatically-query-specific-dns-servers-on-ios/
Below is the revised code for setting up dns -
void setup_dns_server(res_state res, const char *dns_server)
{
res_ninit(res);
struct in_addr6 addr;
// int returnValue = inet_aton(dns_server, &addr);
inet_pton(AF_INET6, dns_server, &addr); // for IPv6 conversion
res->nsaddr_list[0].sin_addr = addr;
res->nsaddr_list[0].sin_family = AF_INET6;
res->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
res->nscount = 1;
};
And the query code -
void query_ip(res_state res, const char *host, char ip[])
{
u_char answer[NS_PACKETSZ];//NS_IN6ADDRSZ NS_PACKETSZ
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
if(ns_msg_count(handle, ns_s_an) > 0) {
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) {
strcpy(ip, inet_ntoa(*(struct in_addr6 *)ns_rr_rdata(rr)));
}
}
}
PS - I have not been able to test it, but it should work for ipv6 dns.

How to Get Host Name connected to a Socket using CFSocketNativeHandle iOS?

I am using Socket programming to connect devices each other in iOS.
I want to get connected device's Host Name which is connected to a socket using CFSocketNativeHandle.
When other device connected to my Socket i get callback in following callback function:
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
// We can only process "connection accepted" calls here
if ( type != kCFSocketAcceptCallBack )
{
return;
}
// for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle*)data;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
NSData *peer = nil;
if (0 == getpeername(nativeSocketHandle, (struct sockaddr *)name, &namelen)) {
peer = [NSData dataWithBytes:name length:namelen];
NSString *hostName = [[NSString alloc] initWithData:peer encoding:NSUTF8StringEncoding];
NSLog(#"HostName=%#",hostName);
}
}
Here I am getting NSData for "peer" but i am getting NSString *hostName=null.
How can i get exact Host name with this NSData.
getpeername() returns the address of the peer connected to the socket. You can access it using code below:
struct sockaddr_in addr ;
socklen_t len = sizeof(addr) ;
int res = getpeername(nativeSocketHandle, (struct sockaddr *)&addr, &len) ;
if (res == 0) {
char strAddr[20] ;
inet_ntop(AF_INET, &addr.sin_addr, strAddr, sizeof(strAddr)) ;
printf("ip %s\n", strAddr) ;
}
The only thing the socket knows is the IP address of the other end of the connection, so when you use getpeername it returns you the IP address. You will need to look up the IP address using either CFhost or POSIX calls - there are a squillion references to how to accomplish this using the POSIX APIs.
There is a very good chance that the other end will not be the same as the name of the device.

Get peer IP address and port on OSX in objective-c from NSStream, CFStream or Socket

I have written a server that listens on a specific port for incoming tcp connections. To manage the network connectivity I am using Streams (CFStream/NSStream). When a connection is esthablished I save all the information about this very connection in another instance of a dedicated class which is also set as the delegate for the streams.
Now I want to get the public IP of the device that sends me a message through the already esthablished streams, in other words, I would like to store the IP of the stream's peer. I tried many things, but unfortunately I only get null-values.
Is there a possibility to get the peer's address (ip and port) from an existing stream of the described form?
Here is some code I have tried:
CFDataRef peerAddress = CFSocketCopyPeerAddress(_sockRef);
// _sockRef is saved when connection is established in listening callback and is not null
I also tried to get the information direct in the listening callback method:
NSData *peer = nil;
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
struct sockaddr *addressinfo = NULL;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(addressinfo);
int result = getpeername(nativeSocketHandle, addressinfo, &namelen);
if (result == 0) {
peer = [NSData dataWithBytes:name length:namelen];
}
struct sockaddr_in *s = (struct sockaddr_in*)name;
char *ipstr = malloc(INET_ADDRSTRLEN);
ipstr = inet_ntoa(s->sin_addr); // is 0.0.0.0 :-(
And I tried another method:
_publicIP = CFWriteStreamCopyProperty((__bridge CFWriteStreamRef)(_writeStream), kCFStreamPropertySocketRemoteHostName);
Why do I always get null-values? Can anyone help me?
Thank you in advance!
Okay I figured it out by now. This is what I did:
- (void)getPublicClientAddress {
// Get public IP from stream
// Get hands on appropriate data structures via the socket number
CFSocketNativeHandle nativeSocketHandle = _socketnumber;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
NSData *peer = nil;
if (0 == getpeername(nativeSocketHandle, (struct sockaddr *)name, &namelen)) {
peer = [NSData dataWithBytes:name length:namelen];
}
if (_ipv6){
// If ipv6 is used
struct sockaddr_in6 *socketaddress = (struct sockaddr_in6*)name;
// convert ip to string
char *ipstr = malloc(INET6_ADDRSTRLEN);
struct in6_addr *ipv6addr = &socketaddress->sin6_addr;
inet_ntop(AF_INET6, ipv6addr, ipstr, sizeof(ipstr));
// convert port to int
int portnumber = socketaddress->sin6_port;
// Save in properties
_publicIP = [NSString stringWithFormat:#"%s", ipstr];
_publicPort = [NSString stringWithFormat:#"%d", portnumber];
} else {
// If ipv4 is used
struct sockaddr_in *socketaddress = (struct sockaddr_in*)name;
// convert ip to string
char *ipstr = malloc(INET_ADDRSTRLEN);
struct in_addr *ipv4addr = &socketaddress->sin_addr;
ipstr = inet_ntoa(*ipv4addr);
//inet_ntop(AF_INET, ipv4addr, ipstr, sizeof(ipstr));
// convert port to int
int portnumber = socketaddress->sin_port;
// Save in properties
_publicIP = [NSString stringWithFormat:#"%s", ipstr];
_publicPort = [NSString stringWithFormat:#"%d", portnumber];
}
}
Afterwards you will have the public IP in the property _publicIP and the public port in the property _publicPort. All information is gathered from a connection on the server side.
I hope this post will help someone =)

Accessing certificates pushed by MDM

I wanted to know is there a way to access certificate pushed by MDM server through your app?
A few years later - is there still no way to access the MDM issued X.509 certificates?
I'm using this code, but getting zero results. And as far as google helps, there is also no way to see if there is any cert at all?
CFTypeRef certificateRef = NULL; // 1
const char *certLabelString = "XenMobile MDM";
CFStringRef certLabel = CFStringCreateWithCString(
NULL, certLabelString,
kCFStringEncodingUTF8);
const void *keys[] = { kSecClass, kSecAttrLabel, kSecReturnRef };
const void *values[] = { kSecClassCertificate, certLabel, kCFBooleanTrue };
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys,
values, 3,
NULL, NULL);
status = SecItemCopyMatching(dict, &certificateRef);
if (status == errSecItemNotFound) {
_UILabelINFO.text = #"error The item cannot be found (errSecItemNotFound) :";
_UILabelINFO.text = [_UILabelINFO.text stringByAppendingString:(__bridge NSString *)(certLabel)];
} else {
_UILabelINFO.text = #"retrieved keychain reference";
}

UDP broadcast using CFSocket on IOS

Have been doing some google search and some reading on this subject but cannot seem to get it right no matter how much time i spent searching.
What i want to do is to receive broadcast message of devices that are connected on my network by advertising my interest in their services that they supply. Using wireshark i can see the broadcast/notification messages from the network devices that i want to connect to sent over my network but not my broadcast search for interest of their services.
But with network utility i can see that the socket is created but don't know which state it is in, whether listen or connected.
Yes i know that there are libraries that i can use to do this, but i wanted to build something of my own from the ground up and get a better understand of how it works.
MySocket.h
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if TARGET_OS_IPHONE
#import <CFNetwork/CFNetwork.h>
#endif
#interface MySocket : NSObject
{
NSString* _message;
CFSocketRef cfSocket;
CFRunLoopSourceRef cfSource;
}
- (void)listen;
#end
MySocket.m
#import "MySocket.h"
#define MAX_UDP_DATAGRAM_SIZE 65507
#implementation MySocket
static void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType
type, CFDataRef address, const void *data, void *userInfo)
{
NSLog(#"socketCAllBAck was called");
}
- (void)listen
{
//Enable broadcast to network hosts
int yes = 1;
int setSockResult = 0;
_message = [[NSMutableString alloc ] initWithString: #"M-SEARCH *HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\nMX:1\r\n\r\n"];
CFSocketContext socketContext = {0, self, NULL, NULL, NULL};
/* Create the server socket as a UDP IPv4 socket and set a callback */
/* for calls to the socket's lower-level accept() function */
cfSocket = CFSocketCreate(NULL, PF_INET, SOCK_DGRAM, IPPROTO_UDP,
kCFSocketAcceptCallBack | kCFSocketDataCallBack , (CFSocketCallBack)socketCallback, &socketContext);
if (cfSocket == NULL)
NSLog(#"UDP socket could not be created\n");
/* Re-use local addresses, if they're still in TIME_WAIT */
setSockResult = setsockopt(CFSocketGetNative(cfSocket), SOL_SOCKET, SO_BROADCAST, (void *)&yes, sizeof(yes));
if(setSockResult < 0)
NSLog(#"Could not setsockopt for broabcast");
/* Set the port and address we want to listen on */
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("239.255.255.250");
//inet_pton(AF_INET, "239.255.255.250", &(addr.sin_addr));
addr.sin_port = htons(1900);
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
if (address != nil && CFSocketSetAddress(cfSocket, (CFDataRef) address) != kCFSocketSuccess) {
NSLog(#"CFSocketSetAddress() failed\n");
CFRelease(cfSocket);
}
CFDataRef data = CFDataCreate(NULL, (const UInt8*)[_message UTF8String], [_message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
setSockResult = CFSocketSendData(cfSocket, (CFDataRef)address, data, 0.0);
if(kCFSocketSuccess != setSockResult) NSLog(#"Unable to send data, %i", setSockResult);
else NSLog(#"Sending data");
cfSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, 0);
if(cfSource == NULL)
NSLog(#"CFRunLoopSourceRef is null");
CFRunLoopAddSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
NSLog(#"Socket listening on port 1900");
CFRelease(cfSource);
CFRelease(cfSocket);
[address release];
data = nil;
CFRunLoopRun();
}
- (void)dealloc
{
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
CFRelease(cfSource);
CFRelease(cfSocket);
[_message release];
[super dealloc];
}
#end
Edit: Everything runs well until the call to send data.
Is there something small but critical in order for this to work that i'm missing?
Or i'm missing the big picture?
Any help or guide is appreciated.
Thanks in advance and have a nice weekend
remove SetAdress and all will be work fine. i've tested this now;
How about use this initialization with address
CFDataRef address=CFDataCreate(kCFAllocatorDefault,(UInt8 *)&addr,sizeof(addr));

Resources