zError function call in zLib impacting performance - ios

When using zlib 1.25 in an iOS project, I've noticed in my profiler (Instruments) that the function zError is being called repeatedly, and is occupying 50% of the overall inflate time.
Does anyone know why zError would be getting invoked like this? I don't call it anywhere in my own code, which is a pretty boilerplate inflate function, pasted below:
int UPNExtractorGZInflate(const void *src, int srcLen, void *dst, int dstLen) {
z_stream strm = {0};
strm.total_in = strm.avail_in = srcLen;
strm.total_out = strm.avail_out = dstLen;
strm.next_in = (Bytef *) src;
strm.next_out = (Bytef *) dst;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int err = -1;
int ret = -1;
err = inflateInit2(&strm, (15 + 16)); //15 window bits, and the +16 tells zlib to decode gzip
if (err == Z_OK) {
err = inflate(&strm, Z_FINISH);
if (err == Z_STREAM_END) {
ret = strm.total_out;
}
else {
inflateEnd(&strm);
return err;
}
}
else {
inflateEnd(&strm);
return err;
}
inflateEnd(&strm);
return ret;
}
And here is the relevant profiler output (notice zError taking 50% of the overall inflate time):

zError isn't called by any zlib function. If you're not calling it, then your profiler is misidentifying the function taking that time.

Related

How to send a chunk of data using shlib(nghttp2) in esp32?

I want to use HTTP2 to POST data continuously. As I found, the only feasible solution is to use shlib. I can implement it and use it. But there were two problems that I faced:
1- shlib does not let us send a data bigger than 16KB theoretically. Here, the solution that I found was to feed the buffer couple of times without calling NGHTTP2_DATA_FLAG_EOF. But, the main problem is that we cannot return the size of the buffer which although is defined as int, but does not support lengths more than 16K.
2- The fault rate of sending data more than about 3 to 4K goes exponentially high as in these situations, just a few of packets are able to be sent correctly.
Any suggestion?
Thanks
I did all my bests to make sure that the resources don't interfere with each other. Here is my code:
#include <Arduino.h>
#include <WiFiClientSecure.h>
#include "esp_camera.h"
extern "C"
{
#include "sh2lib.h"
}
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
const char* ssid = "NETWORK"; // your network SSID (name of wifi network)
const char* password = "PASSWORD"; // your network password
bool request_finished = false;
String head = "--JPEG_IMAGE\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--JPEG_IMAGE--\r\n";
char data_to_post[16000];
uint32_t totalLen;
camera_config_t config;
struct sh2lib_handle hd;
bool is_captured;
bool is_posted;
uint16_t safety_counter;
void setup()
{
// put your setup code here, to run once:
Serial.begin(115200);
delay(100);
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// attempt to connect to Wifi network:
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
// wait 1 second for re-trying
delay(1000);
}
Serial.print("\n");
Serial.print(F("Connected to: "));
Serial.println(ssid);
// Etablishing Connection
Serial.println(F("Establishing Connection... "));
if (sh2lib_connect(&hd, "My_Server") != ESP_OK)
{
Serial.println("Error connecting to HTTP2 server");
//vTaskDelete(NULL);
}
Serial.println(F("Connected to the webserver"));
delay(1000);
// Configuring the Cam
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
// init with high specs to pre-allocate larger buffers
if(psramFound())
{
config.frame_size = FRAMESIZE_VGA;// FRAMESIZE_QVGA
config.jpeg_quality = 10; //0-63 lower number means higher quality
config.fb_count = 2;
}
else
{
config.frame_size = FRAMESIZE_CIF;
config.jpeg_quality = 12; //0-63 lower number means higher quality
config.fb_count = 1;
}
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK)
{
Serial.printf("Camera init failed with error 0x%x", err);
delay(1000);
ESP.restart();
}
// This task handles the POST requests
xTaskCreatePinnedToCore(
task_http2
, "http2_task"
, (1024 * 24) // Stack size
, NULL
, 3 // Priority
, NULL
, ARDUINO_RUNNING_CORE);
Serial.println(F("Task Called"));
}
void loop()
{
// To prohibit the interference between resources, I used main loop to capture the Images
// Check if the posting has been finished
if (is_posted)
{
Serial.println(F("Call to Capture"));
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb)
{
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
}
// to check if the size is not bigger than 16K
uint32_t imageLen = fb->len;
if(imageLen<16000)
{
// Creating the body of the post
uint32_t extraLen = tail.length()+head.length();
totalLen = extraLen + imageLen;
uint8_t *fbBuf = fb->buf;
const char* head_char = head.c_str();
const char* tail_char = tail.c_str();
uint32_t totalLen_copy = totalLen;
char alpha[totalLen];
std::copy(head_char,head_char+head.length(), data_to_post);
std::copy(fbBuf,fbBuf+imageLen , data_to_post+head.length());
std::copy(tail_char,tail_char+ tail.length(), data_to_post+head.length()+imageLen);
esp_camera_fb_return(fb);
Serial.println(F("Camera captured"));
delay(100);
safety_counter++;
// Stopping capturing until posting is finished
is_captured = true;
is_posted = false;
}
}
delay(100);
}
int handle_get_response(struct sh2lib_handle *handle, const char *data, size_t len, int flags)
{
if (len > 0)
{
Serial.printf("%.*s\n", len, data);
}
if (flags == DATA_RECV_RST_STREAM)
{
Serial.println("STREAM CLOSED");
}
return 0;
}
int handle_post_response(struct sh2lib_handle *handle, const char *data, size_t len, int flags)
{
if (len > 0) {
Serial.printf("%.*s\n", len, data);
// decreasing the counter to prevent fault loop
safety_counter--;
}
//Serial.print(F("Safety_Counter in Response: ")); Serial.println(safety_counter);
if (flags == DATA_RECV_RST_STREAM) {
request_finished = true;
Serial.println("STREAM CLOSED");
}
return 0;
}
int send_post_data(struct sh2lib_handle *handle, char *buf, size_t length, uint32_t *data_flags)
{
// To check the body of the post
/*
Serial.println("Post Buffer");
for(int i;i<totalLen;i++)
Serial.print(data_to_post[i]);
Serial.println("Post Buffer End");
*/
if (totalLen < length)
{
memcpy(buf, data_to_post, totalLen);
}
else
{
Serial.println("Cannot write to buffer");
//copylen = 0;
}
(*data_flags) |= NGHTTP2_DATA_FLAG_EOF;
return totalLen;
}
void task_http2(void *args)
{
Serial.println(F("Task Runs"));
// Start capturing
is_posted = true;
int counter = 0;
for(;;)
{
// if capturing finished:
if(is_captured)
{
// after each five unsuccessful posts, reestablish the connection
Serial.print(F("Safety_Counter is: ")); Serial.println(safety_counter);
if(safety_counter>5)
{
is_posted = false;
vTaskDelay(100);
counter = 0;
safety_counter = 0;
sh2lib_free(&hd);
vTaskDelay(100);
Serial.println(F("Safety Counter Occured ... "));
if (sh2lib_connect(&hd, "My_Server") != ESP_OK)
{
Serial.println("Error connecting to HTTP2 server");
//vTaskDelete(NULL);
}
Serial.println(F("Connected to the webserver"));
vTaskDelay(1000);
// Preparing capturing again
is_posted = true;
is_captured = false;
continue;
}
char len[20];
sprintf(len, "%d",totalLen); //length_of_body);
Serial.print("the length is: ");
Serial.println(len);
const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "POST"),
SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd.hostname),
SH2LIB_MAKE_NV(":path", "/mvp/upload_image"),
SH2LIB_MAKE_NV("Content-Length", len),
SH2LIB_MAKE_NV("Content-Type", "multipart/form-data; boundary=JPEG_IMAGE")
};
sh2lib_do_putpost_with_nv(&hd, nva, sizeof(nva) / sizeof(nva[0]), send_post_data, handle_post_response);
while (1)
{
if (sh2lib_execute(&hd) != ESP_OK)
{
Serial.println("Error in execute");
break;
}
if (request_finished)
{
// a general counter to reestablish the connection
counter++;
break;
}
//vTaskDelay(1000);
}
}
// General counter
if(counter>30)
{
counter = 0;
sh2lib_free(&hd);
vTaskDelay(100);
Serial.println(F("Establishing Connection... "));
if (sh2lib_connect(&hd, "My_Server") != ESP_OK)
{
Serial.println("Error connecting to HTTP2 server");
//vTaskDelete(NULL);
}
Serial.println(F("Connected to the webserver"));
}
is_captured = false;
is_posted = true;
Serial.println("Sending finished");
vTaskDelay(1000);
}
}

AudioConverter#FillComplexBuffer returns -50 and does not convert anything

I'm strongly following this Xamarin sample (based on this Apple sample) to convert a LinearPCM file to an AAC file.
The sample works great, but implemented in my project, the FillComplexBuffer method returns error -50 and the InputData event is not triggered once, thus nothing is converted.
The error only appears when testing on a device. When testing on the emulator, everything goes great and I get a good encoded AAC file at the end.
I tried a lot of things today, and I don't see any difference between my code and the sample code. Do you have any idea where this may come from?
I don't know if this is in anyway related to Xamarin, it doesn't seem so since the Xamarin sample works great.
Here's the relevant part of my code:
protected void Encode(string path)
{
// In class setup. File at TempWavFilePath has DecodedFormat as format.
//
// DecodedFormat = AudioStreamBasicDescription.CreateLinearPCM();
// AudioStreamBasicDescription encodedFormat = new AudioStreamBasicDescription()
// {
// Format = AudioFormatType.MPEG4AAC,
// SampleRate = DecodedFormat.SampleRate,
// ChannelsPerFrame = DecodedFormat.ChannelsPerFrame,
// };
// AudioStreamBasicDescription.GetFormatInfo (ref encodedFormat);
// EncodedFormat = encodedFormat;
// Setup converter
AudioStreamBasicDescription inputFormat = DecodedFormat;
AudioStreamBasicDescription outputFormat = EncodedFormat;
AudioConverterError converterCreateError;
AudioConverter converter = AudioConverter.Create(inputFormat, outputFormat, out converterCreateError);
if (converterCreateError != AudioConverterError.None)
{
Console.WriteLine("Converter creation error: " + converterCreateError);
}
converter.EncodeBitRate = 192000; // AAC 192kbps
// get the actual formats back from the Audio Converter
inputFormat = converter.CurrentInputStreamDescription;
outputFormat = converter.CurrentOutputStreamDescription;
/*** INPUT ***/
AudioFile inputFile = AudioFile.OpenRead(NSUrl.FromFilename(TempWavFilePath));
// init buffer
const int inputBufferBytesSize = 32768;
IntPtr inputBufferPtr = Marshal.AllocHGlobal(inputBufferBytesSize);
// calc number of packets per read
int inputSizePerPacket = inputFormat.BytesPerPacket;
int inputBufferPacketSize = inputBufferBytesSize / inputSizePerPacket;
AudioStreamPacketDescription[] inputPacketDescriptions = null;
// init position
long inputFilePosition = 0;
// define input delegate
converter.InputData += delegate(ref int numberDataPackets, AudioBuffers data, ref AudioStreamPacketDescription[] dataPacketDescription)
{
// how much to read
if (numberDataPackets > inputBufferPacketSize)
{
numberDataPackets = inputBufferPacketSize;
}
// read from the file
int outNumBytes;
AudioFileError readError = inputFile.ReadPackets(false, out outNumBytes, inputPacketDescriptions, inputFilePosition, ref numberDataPackets, inputBufferPtr);
if (readError != 0)
{
Console.WriteLine("Read error: " + readError);
}
// advance input file packet position
inputFilePosition += numberDataPackets;
// put the data pointer into the buffer list
data.SetData(0, inputBufferPtr, outNumBytes);
// add packet descriptions if required
if (dataPacketDescription != null)
{
if (inputPacketDescriptions != null)
{
dataPacketDescription = inputPacketDescriptions;
}
else
{
dataPacketDescription = null;
}
}
return AudioConverterError.None;
};
/*** OUTPUT ***/
// create the destination file
var outputFile = AudioFile.Create (NSUrl.FromFilename(path), AudioFileType.M4A, outputFormat, AudioFileFlags.EraseFlags);
// init buffer
const int outputBufferBytesSize = 32768;
IntPtr outputBufferPtr = Marshal.AllocHGlobal(outputBufferBytesSize);
AudioBuffers buffers = new AudioBuffers(1);
// calc number of packet per write
int outputSizePerPacket = outputFormat.BytesPerPacket;
AudioStreamPacketDescription[] outputPacketDescriptions = null;
if (outputSizePerPacket == 0) {
// if the destination format is VBR, we need to get max size per packet from the converter
outputSizePerPacket = (int)converter.MaximumOutputPacketSize;
// allocate memory for the PacketDescription structures describing the layout of each packet
outputPacketDescriptions = new AudioStreamPacketDescription [outputBufferBytesSize / outputSizePerPacket];
}
int outputBufferPacketSize = outputBufferBytesSize / outputSizePerPacket;
// init position
long outputFilePosition = 0;
long totalOutputFrames = 0; // used for debugging
// write magic cookie if necessary
if (converter.CompressionMagicCookie != null && converter.CompressionMagicCookie.Length != 0)
{
outputFile.MagicCookie = converter.CompressionMagicCookie;
}
// loop to convert data
Console.WriteLine ("Converting...");
while (true)
{
// create buffer
buffers[0] = new AudioBuffer()
{
NumberChannels = outputFormat.ChannelsPerFrame,
DataByteSize = outputBufferBytesSize,
Data = outputBufferPtr
};
int writtenPackets = outputBufferPacketSize;
// LET'S CONVERT (it's about time...)
AudioConverterError converterFillError = converter.FillComplexBuffer(ref writtenPackets, buffers, outputPacketDescriptions);
if (converterFillError != AudioConverterError.None)
{
Console.WriteLine("FillComplexBuffer error: " + converterFillError);
}
if (writtenPackets == 0) // EOF
{
break;
}
// write to output file
int inNumBytes = buffers[0].DataByteSize;
AudioFileError writeError = outputFile.WritePackets(false, inNumBytes, outputPacketDescriptions, outputFilePosition, ref writtenPackets, outputBufferPtr);
if (writeError != 0)
{
Console.WriteLine("WritePackets error: {0}", writeError);
}
// advance output file packet position
outputFilePosition += writtenPackets;
if (FlowFormat.FramesPerPacket != 0) {
// the format has constant frames per packet
totalOutputFrames += (writtenPackets * FlowFormat.FramesPerPacket);
} else {
// variable frames per packet require doing this for each packet (adding up the number of sample frames of data in each packet)
for (var i = 0; i < writtenPackets; ++i)
{
totalOutputFrames += outputPacketDescriptions[i].VariableFramesInPacket;
}
}
}
// write out any of the leading and trailing frames for compressed formats only
if (outputFormat.BitsPerChannel == 0)
{
Console.WriteLine("Total number of output frames counted: {0}", totalOutputFrames);
WritePacketTableInfo(converter, outputFile);
}
// write the cookie again - sometimes codecs will update cookies at the end of a conversion
if (converter.CompressionMagicCookie != null && converter.CompressionMagicCookie.Length != 0)
{
outputFile.MagicCookie = converter.CompressionMagicCookie;
}
// Clean everything
Marshal.FreeHGlobal(inputBufferPtr);
Marshal.FreeHGlobal(outputBufferPtr);
converter.Dispose();
outputFile.Dispose();
// Remove temp file
File.Delete(TempWavFilePath);
}
I already saw this SO question, but the not-detailed C++/Obj-C related answer doesn't seem to fit with my problem.
Thanks !
I finally found the solution!
I just had to declare AVAudioSession category before converting the file.
AVAudioSession.SharedInstance().SetCategory(AVAudioSessionCategory.AudioProcessing);
AVAudioSession.SharedInstance().SetActive(true);
Since I also use an AudioQueue to RenderOffline, I must in fact set the category to AVAudioSessionCategory.PlayAndRecord so both the offline rendering and the audio converting work.

Backtracing the usermode stack of a thread during a minifilter callback function

I'm trying to backtrace the usermode stack of a thread during a minifilter callback function.
Assuming that I'm in the same context as the calling thread, getting the thread stack address from it's TEB/TIB and processing the addresses on that stack should allow me to backtrace it's stack.
Since the addresses I'm getting are not the expected usermode modules that are calling the system-call,
there must be something wrong I'm doing.
I will be glad if you would point me to the correct direction.
Thanks in advance.
Following is the code that is reading the stack content:
pTEB = (PVOID *)((char *)pThread + 0x20);
// Read TIB
pTib = (NT_TIB*)pTEB;
stackBottom = (PVOID*)pTib->StackLimit;
stackTop = (PVOID*)pTib->StackBase;
LogDbgView(("stackBottom=%p, stackTop=%p",stackBottom, stackTop));
if (!MyReadMemory(IoGetCurrentProcess(), stackBottom, buf, stackTop-stackBottom))
{
LogDbgView(("Read Memory = %x",buf));
LogDbgView(("Read Memory = %x",buf+8));
LogDbgView(("Read Memory = %x",buf+16));
LogDbgView(("Read Memory = %x",buf+24));
}
Below are the functions that gets the module names and addresses:
PVOID LoadModulesInformation()
{
PVOID pSystemInfoBuffer = NULL;
__try
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
ULONG SystemInfoBufferSize = 0;
status = ZwQuerySystemInformation(SystemModuleInformation,
&SystemInfoBufferSize,
0,
&SystemInfoBufferSize);
if (!SystemInfoBufferSize)
return NULL;
pSystemInfoBuffer = (PVOID)ExAllocatePool(NonPagedPool, SystemInfoBufferSize*2);
if (!pSystemInfoBuffer)
return NULL;
memset(pSystemInfoBuffer, 0, SystemInfoBufferSize*2);
status = ZwQuerySystemInformation(SystemModuleInformation,
pSystemInfoBuffer,
SystemInfoBufferSize*2,
&SystemInfoBufferSize);
if (NT_SUCCESS(status))
{
return pSystemInfoBuffer;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
return NULL;
}
PUNICODE_STRING findModuleName(PVOID addr, PVOID pSystemInfoBuffer, ULONG Tag FILE_AND_LINE_ARGS)
{
PVOID pModuleBase = NULL;
PCHAR pCharRet=NULL;
PUNICODE_STRING pus = NULL;
__try
{
if (pSystemInfoBuffer != NULL && MmIsAddressValid(addr))
{
PSYSTEM_MODULE_ENTRY pSysModuleEntry = ((PSYSTEM_MODULE_INFORMATION)(pSystemInfoBuffer))->Module;
ULONG i;
for (i = 0; i <((PSYSTEM_MODULE_INFORMATION)(pSystemInfoBuffer))->Count; i++)
{
if ((pSysModuleEntry[i].Base <= addr) && (pSysModuleEntry[i].Size < ((ULONG)addr - (ULONG)pSysModuleEntry[i].Base)))
{
pCharRet = pSysModuleEntry[i].ImageName+pSysModuleEntry[i].PathLength;
break;
}
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
pCharRet = NULL;
}
if (pCharRet)
{
pus = UtlpCharToUnicode(pCharRet, TRUE, TRUE, Tag FILE_AND_LINE_PARAMS);
}
else
{
pus = UtlpCharToUnicode("UNKNOWN", TRUE, TRUE, Tag FILE_AND_LINE_PARAMS);
}
return pus;
}
pTEB = (PVOID *)((char *)pThread + 0x20);
Don't do this at all, even in PoC. Structure layout changes time to time. You can use PsGetProcessPeb instead (see here how https://code.google.com/p/arkitlib/source/browse/trunk/ARKitDrv/Ps.c)
LogDbgView(("Read Memory = %x",buf));
You don`t read memory at buf address, you read value of buf instead. In C you must dereference pointer to read memory from there. Like this
LogDbgView(("Read Memory = %x",(PVOID)*buf));
LogDbgView(("Read Memory = %x",buf+8));
To be able to compile for x86 as well as for x64 you must avoid such things. Instead, use sizeof(PVOID):
LogDbgView(("Read Memory = %x",(PVOID)*(buf+sizeof(PVOID))));

Having trouble with kern_return_t error

I am getting the following error when I try to compile with the code below it. I don't know how to fix it. I have googled. Also asked others, but nobody has been able to help me.
Compiling on mac through theos.
I would really appreciate it if someone can help me.
Error output
error: assigning to
'kern_return_t' (aka 'int') from incompatible type 'uint *'
(aka 'unsigned int *')
err = reinterpret_cast<uint *>(address* sizeof(data));
Code
bool writeData(vm_address_t address, string str) {
//declaring variables
kern_return_t err = 0;
mach_port_t port = mach_task_self();
size_t find = str.find("0x");
if (find != -1) {
str.replace(find, 2, "");
}
int len = str.size();
char data[len / 2];
int x = 0;
for(int i = 0; i < len; i += 2) {
string dat = str.substr(i, 2);
uint8_t tmp;
sscanf(dat.c_str(), "%hhx", &tmp);
data[x] = tmp;
x++;
}
//set memory protections to allow us writing code there
err = vm_protect(port, (vm_address_t)address, sizeof(data), NO, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
//check if the protection fails
if (err != KERN_SUCCESS)
return FALSE;
//write code to memory
err = reinterpret_cast<uint64_t *>(address* sizeof(data));
if (err != KERN_SUCCESS)
return FALSE;
//set the protections back to normal so the app can access this address as usual
err = vm_protect(port, (vm_address_t)address, sizeof(data), NO, VM_PROT_READ | VM_PROT_EXECUTE);
if (err != KERN_SUCCESS)
return FALSE;
return TRUE;
}

Query DNS to find NAPTR in iOS

I have been having a lot of trouble finding a way to query the DNS to find the NAPTR in iOS. There seem to be many relatively simple ways to resolve to an IP, but I specifically need to find all NAPTR records in a DNS lookup. I'd prefer to do so without having to bring in any external libraries if at all possible. If anyone has been able to do this (or something similar that I can extrapolate from) I'd appreciate any pointers.
All code must function in iOS 5.0+
I ended up using DNSServiceQueryRecord.
DNSServiceRef sdRef;
DNSServiceQueryRecord(&sdRef, 0, 0,
"google.com",
kDNSServiceType_NAPTR,
kDNSServiceClass_IN,
callback,
NULL);
DNSServiceProcessResult(sdRef);
DNSServiceRefDeallocate(sdRef);
In actual use, I found that there was an issue where the app would hang indefinitely if there were no results, so I ended up having to adjust my code to add a timeout on the result.
/*
Attempt to fetch the NAPTR from the stored server address. Since iOS will continue waiting
until told directly to stop (even if there is no result) we must set our own timeout on the
request (set to 5 seconds).
On success, the callback function is called. On timeout, the kSRVLookupComplete notification
is sent.
*/
- (void)attemptNAPTRFetch {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
DNSServiceRef sdRef;
DNSServiceErrorType err;
err = DNSServiceQueryRecord(&sdRef, 0, 0,
[server cStringUsingEncoding:[NSString defaultCStringEncoding]],
kDNSServiceType_NAPTR,
kDNSServiceClass_IN,
callback,
NULL);
// This stuff is necessary so we don't hang forever if there are no results
int dns_sd_fd = DNSServiceRefSockFD(sdRef);
int nfds = dns_sd_fd + 1;
fd_set readfds;
struct timeval tv;
int result;
int stopNow = 0;
int timeOut = 5; // Timeout in seconds
while (!stopNow) {
FD_ZERO(&readfds);
FD_SET(dns_sd_fd, &readfds);
tv.tv_sec = timeOut;
tv.tv_usec = 0;
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (result > 0) {
if(FD_ISSET(dns_sd_fd, &readfds)) {
err = DNSServiceProcessResult(sdRef);
if (err != kDNSServiceErr_NoError){
NSLog(#"There was an error");
}
stopNow = 1;
}
}
else {
printf("select() returned %d errno %d %s\n", result, errno, strerror(errno));
if (errno != EINTR) {
stopNow = 1;
postNotification(kSRVLookupComplete, nil);
}
}
}
DNSServiceRefDeallocate(sdRef);
});
}
Then, for the callback:
static void callback(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void *rdata,
uint32_t ttl,
void *context)
{
uint16_t order, pref;
char flag;
NSMutableString *service = [[NSMutableString alloc] init];
NSMutableString *replacement = [[NSMutableString alloc] init];
const char *data = (const char*)rdata;
order = data[1];
pref = data[3];
flag = data[5];
int i = 7;
while (data[i] != 0){
[service appendString:[NSString stringWithFormat:#"%c", data[i]]];
i++;
}
i += 2;
while(data[i] != 0){
if(data[i] >= 32 && data[i] <= 127)
[replacement appendString:[NSString stringWithFormat:#"%c", data[i]]];
else
[replacement appendString:#"."];
i++;
}
NSLog(#"\nOrder: %i\nPreference: %i\nFlag: %c\nService: %#\nReplacement: %#\n", order, pref, flag, service, replacement);
}
This seems to do the trick for me. You would of course do any other necessary work using all the parsed data in the callback or store the data somewhere to be used later.

Resources