Hi,
I am working on an iOS app which requires to resolve DNS programmatically.
Consider this as a proxy to resolve all dns queries on iPhone. I receive DNS queries from each app on iPhone and send back corresponding IPList
I have tried a couple of methods but both have same kind of responses. The one I decided to move with is given below resolveHost function written in objective-c and c I am calling this method from swift code.
This is how I am calling from swift, also sharing sample host/url, value of host can be any domain ("google.com, apple.com etc") or a domain/host as a result of trails when you open a site in mkwebview
let host = "www.opera.com"
let ipArray = ResolveUtil().resolveHost(host, usingDNSServer: "8.8.8.8") as! [String]
More specifically Facebook app does not work well with IPs returned from function resolveHost
By not working well I mean app does not connect to IPs returned from the functions
Some times it returns 192.16.192.16 as part of other IPs for some hosts/domains. What is this IP?
- (NSArray*)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer
{
NSMutableArray* result = [[NSMutableArray alloc] init];
struct __res_state res;
setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
int count;
char** ips = query_ips(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], &count);
for (int i=0; i<count; i++){
[result addObject:[[NSString alloc] initWithCString:ips[i] encoding:NSASCIIStringEncoding]];
}
for (int i=0; i<count; i++){
free(ips[i]);
}
free(ips);
ips = NULL;
return result;
}
char ** query_ips(res_state res, const char *host, int* count)
{
u_char answer[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);
int messageCount = ns_msg_count(handle, ns_s_an);
*count = messageCount;
char **ips = malloc(messageCount * sizeof(char *));
for (int i=0; i < messageCount; i++) {
ips[i] = malloc(16 * sizeof(char));
memset(ips[i], '\0', sizeof(16));
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
return ips;
}
Other Method
func resolveIp(_ hostUrl:String) -> [String]{
var ips:[String] = [String]()
let host = CFHostCreateWithName(nil,hostUrl as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
for case let theAddress as NSData in addresses {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
let numAddress = String(cString: hostname)
ips.append(numAddress)
}
}
}
Logger.info("\(#function) validIPs:\(ips.joined(separator: "-")) url:\(hostUrl)")
return ips
}
192.16.192.16
What is this IP? Perfectly valid IPv4. It resolves back to basento.nikhef.nl.
Why is it returned?
I don't know. Maybe, see resolv.h:
* Mac OS supports a DNS query routing API (see <dns.h>) which is used by
* most system services to access DNS. The BIND-9 APIs described here are
* a lower-level that does not do query routing or search amongst multiple
* resolver clients. The results of DNS queries from this API may differ
* significantly from the results of queries sent to the <dns.h> API. We
* strongly encourage developers to use higher-level APIs where possible.
By not working well I mean app does not connect to IPs returned from the functions.
This has nothing to do with the name/ip address resolution.
The problem can be elsewhere. Your provider can block it, no service is running on the IP address, you're not allowed to access it, ... Many reasons.
More specifically Facebook app does not work well with IPs returned from function resolveHost.
This doesn't make sense to me at all. You have your own app, you're resolving IP addresses in it and then saying that it doesn't work with Facebook. Frankly, I have no idea what do you mean with this.
Why am I answering this question? Well, you shouldn't blindly copy & paste code from other Stack Overflow questions or any other sites. Did a research and it looks like a copy & paste of some other answers.
Why? The code in your question doesn't handle errors, doesn't follow documentation, ... It's a pure luck that it works for you.
What if this is your problem? Did you ever consider this option?
Here's an example of Resolver you can use / test with your conditions. It may or may not fix your issues.
#import <resolv.h>
#import Darwin.POSIX.arpa;
#interface Resolver: NSObject
- (nullable instancetype)initWithDNSServer:(nonnull NSString *)server;
- (nullable NSArray<NSString *> *)resolveHost:(nonnull NSString *)host;
#end
#implementation Resolver {
struct __res_state *state;
}
- (void)dealloc {
if (state != NULL) {
// man 3 resolver:
//
// res_ndestroy() should be call to free memory allocated by res_ninit() after last use.
if ((state->options & RES_INIT) == RES_INIT) {
res_ndestroy(state);
}
free(state);
state = NULL;
}
}
- (nullable instancetype)initWithDNSServer:(nonnull NSString *)server {
if ((self = [super init]) == nil) {
return nil;
}
// man 3 resolver:
//
// The memory referred to by statp must be set to all zeros prior
// to the first call to res_ninit(). res_ndestroy() should be call to free memory
// allocated by res_ninit() after last use.
if ((state = calloc(1, sizeof(*state))) == NULL) {
return nil;
}
// 0 success
if (res_ninit(state) != 0) {
return nil;
}
// Avoid calling inet_aton later with NULL if we can't convert it to ASCII
if (![server canBeConvertedToEncoding:NSASCIIStringEncoding]) {
return nil;
}
struct in_addr addr;
// man 3 inet_aton:
//
// It returns 1 if the string was successfully interpreted ...
if (inet_aton([server cStringUsingEncoding:NSASCIIStringEncoding], &addr) != 1) {
return nil;
}
state->nsaddr_list[0].sin_addr = addr;
state->nsaddr_list[0].sin_family = AF_INET;
state->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
state->nscount = 1;
return self;
}
- (nullable NSArray<NSString *> *)resolveHost:(nonnull NSString *)host {
// Avoid calling res_nquery with NULL
if (![host canBeConvertedToEncoding:NSASCIIStringEncoding]) {
return nil;
}
u_char answer[NS_PACKETSZ];
int len = res_nquery(state, [host cStringUsingEncoding:NSASCIIStringEncoding],
ns_c_in, ns_t_a, answer, sizeof(answer));
// -1 = error
if (len == -1) {
return nil;
}
ns_msg handle;
// 0 success, -1 error
if (ns_initparse(answer, len, &handle) != 0) {
return nil;
}
u_int16_t count = ns_msg_count(handle, ns_s_an);
NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
for (int i = 0 ; i < count ; i++) {
ns_rr rr;
// 0 success, -1 error
if (ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
char *address = inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr));
if (address == NULL) {
continue;
}
NSString *ip = [NSString stringWithCString:address
encoding:NSASCIIStringEncoding];
[result addObject:ip];
}
}
return result;
}
#end
You can use it in this way:
Resolver *resolver = [[Resolver alloc] initWithDNSServer:#"8.8.8.8"];
NSLog(#"%#", [resolver resolveHost:#"www.opera.com"]);
Output:
(
"13.102.114.111",
"52.57.141.185",
"18.196.127.98"
)
I might have not explained the problem statement in my original question but I managed to fix the bug, so I thought I should write here my findings.
My app works as a dns proxy, so its main responsibility was to resolve domains and return IPs.
I used resolveHost function to resolve the IP. This function has all the issues mentioned by zrzka so if somebody wants to use please do consider his points.
The problem I had was that the function returns a few IPs against specific hosts/domains which does not seem valid, I am saying invalid because these were not pingable IPs and from Wireshark I confirmed connection on these IPs were unsuccessful, even if returned IPList contains valid IP at some index it was still causing unnecessary delay due to first try on invalid IPs as they reside before valid IPs in the list.
On further investigation I came to know these invalid IPs were against answer type CNAME which depicts Alias in DNS record, I don't know I should still call them invalid or not but ignoring them did the job for me. Now I only accept A type or AAAA type answers from DNS response. I have achieved this by a simple check in the following function.
char ** query_ips(res_state res, const char *host, int* count)
{
u_char answer[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);
int messageCount = ns_msg_count(handle, ns_s_an);
*count = messageCount;
char **ips = malloc(messageCount * sizeof(char *));
for (int i=0; i < messageCount; i++) {
ips[i] = malloc(16 * sizeof(char));
memset(ips[i], '\0', sizeof(16));
ns_rr rr;
if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
if (1 == rr.type || 28 == rr.type) // here is the new check
strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
}
}
return ips;
}
The documentation says:
void g_ptr_array_insert (GPtrArray *array,
gint index_,
gpointer data);
Inserts an element into the pointer array at the given index. The
array will grow in size automatically if necessary.
Same question for g_ptr_array_add().
Documentation is silent about thread safety of these functions. The following functions are described expressly to be thread-safe:
g_ptr_array_free ()
g_ptr_array_ref()
g_ptr_array_unref ()
What if multiple-threads execute g_ptr_array_insert() at the same time on the same array of pointers? Do I have to provide thread safety myself?
No, it's not thread-safe (just as almost all GLib data types; source, see “you must coordinate accesses…”). Chances are that
two threads enter marked line simultaneously.
static void
g_ptr_array_maybe_expand (GRealPtrArray *array,
gint len)
{
if ((array->len + len) > array->alloc)
{
guint old_alloc = array->alloc;
array->alloc = g_nearest_pow (array->len + len);
array->alloc = MAX (array->alloc, MIN_ARRAY_SIZE);
array->pdata = g_realloc (array->pdata, sizeof (gpointer) * array->alloc); // here
if (G_UNLIKELY (g_mem_gc_friendly))
for ( ; old_alloc < array->alloc; old_alloc++)
array->pdata [old_alloc] = NULL;
}
}
void
g_ptr_array_add (GPtrArray *array,
gpointer data)
{
GRealPtrArray *rarray = (GRealPtrArray *)array;
g_return_if_fail (rarray);
g_ptr_array_maybe_expand (rarray, 1);
rarray->pdata[rarray->len++] = data;
}
Provide your own locking using, for example, GMutex.
I am developing a game app which will take structured data from a server and make responses based on these data.
I have connected the app to the internet through the NSSream. Specifically, I follow the tutorial of Apple's guide. So my code looks something like:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if(!_data) {
_data = [[NSMutableData data] retain];
}
uint8_t buf[1024];
NSInteger len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[_data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
[bytesRead setIntValue:[bytesRead intValue]+len];
} else {
NSLog(#"no buffer!");
}
break;
}
// continued
My problem is: "How can I convert the _data to the format I want when it is possible". For example, my server will send two types of data. For each data, the first byte is an indicater for the data type (e.g., 1 means data type 1, 2 means data type 2). For the data type 1, an int (4 bytes) is sent as the data itself. For the data type 2, an int (4 bytes) is sent as the size of the following string bytes. For example, if this int is 10, then there will be 10 more bytes sent from the server to form a string for the client.
The code in my Android(Java) app looks like:
// dataIn = new DataInputStream(socket.getInputStream());
private void keepPacketRecving(){ // this will be executed in a separate thread
while(keepRecvThreadRunning) {
try {
byte type;
type = dataIn.readByte();
if(type == 1){
int data = dataIn.readInt();
getTye1Data(data); // callback function for receiving type 1 data (int)
} else if (type ==2) {
int dataSize = dataIn.readInt();
byte[] buf = new byte[dataSize];
// ... loop to read enough byte into buf ...
String data = new String(buf);
getType2Data(data); // callback function for receiving type 2 data (String)
}
}
}
}
I also notice I can't have a while-loop inside the switch statement to ask inputStream to read more data untile it is enougth because it will hang the thread.
I guess the best way would be always appending all the bytes into the _data (like the example) and has another function to parse the _data? Then the problem is how I can peek bytes from the data and just fetch part of it out (e.g., 10 bytes out of a 20-byte _data).
Is there any wrapper in Objective-C like the DataInputStream in Java?
I'm trying to parse value from string in C for GLib application.
The result is well-compiled freezing app with no error/warning.
I post the function used to be launched for specific thread. The function is a stub only.
int i;
gchar *buffer="";
void *mainthreadfunc(){
buffer = g_strdup ("25");
i = atoi((char *)buffer);
for(;i<10;i++){
g_print("The i is: %d\n", i);
g_usleep (1000000);
}
}
I am writing a plugins subsystem and one of the ideas is to iterate through a dylib (or at least current global scope) exported functions. I know there are other ways, just really want to give this one a try.
What I am wondering, is there a way to get a list of functions exported by a dylib or available in global scope through OS X and iOS API?
Thanks in advance!
You can use a command 'nm' for getting an information from a dynamic library.
See additionally system manual for this command on Mac.
If you are looking to do that from code, you could use this method.
std::vector<std::string> load_mach_o(std::string file_name)
{
/*
Parse the Mach-O structure to find all the exported symbols
Mach-O structure:
mach_header_64
cmd
...
cmd
data
...
data
*/
std::vector<std::string> methods;
off_t offset = sizeof(struct mach_header_64);
BYTE * bytes = load_bytes(file_name.c_str());
if (bytes == NULL)
{
return methods;
}
struct mach_header_64 *header = (struct mach_header_64 *)bytes;
//Get the load commands
struct load_command *cmd = (struct load_command *)(bytes + offset);
for (uint32_t i = 0U; i < header->ncmds; i++)
{
if (cmd->cmd == LC_SYMTAB)
{
struct symtab_command * symtab = (struct symtab_command *)cmd;
off_t string_start = 0;
const char* strings = (const char *)(bytes + symtab->stroff + 1);
for (uint32_t i = 0 ; i < symtab->strsize ; i++)
{
if (strings[i] == '\0')
{
i++;
size_t size = sizeof(char) * (i - string_start);
if (size == 1)
{
string_start = i+1;
continue;
}
methods.push_back(std::string((const char *)(strings + string_start)));
string_start = i+1;
}
}
}
offset += cmd->cmdsize;
//load next command
cmd = (struct load_command *)(bytes + offset);
}
free(bytes);
return methods;
}
This function read the file and parses the structure till mach-O strings section, then, parses each string and store it in a vector containing all the exposed functions.
Best regards.