How to validate an IP address with regular expression in Objective-C? - objective-c

How to validate an IP address in Objective-C?

Here's a category using the modern inet_pton which will return YES for a valid IPv4 or IPv6 string.
#include <arpa/inet.h>
#implementation NSString (IPValidation)
- (BOOL)isValidIPAddress
{
const char *utf8 = [self UTF8String];
int success;
struct in_addr dst;
success = inet_pton(AF_INET, utf8, &dst);
if (success != 1) {
struct in6_addr dst6;
success = inet_pton(AF_INET6, utf8, &dst6);
}
return success == 1;
}
#end

Here's an alternative approach that might also help. Let's assume you have an NSString* that contains your IP address, called ipAddressStr, of the format a.b.c.d:
int ipQuads[4];
const char *ipAddress = [ipAddressStr cStringUsingEncoding:NSUTF8StringEncoding];
sscanf(ipAddress, "%d.%d.%d.%d", &ipQuads[0], &ipQuads[1], &ipQuads[2], &ipQuads[3]);
#try {
for (int quad = 0; quad < 4; quad++) {
if ((ipQuads[quad] < 0) || (ipQuads[quad] > 255)) {
NSException *ipException = [NSException
exceptionWithName:#"IPNotFormattedCorrectly"
reason:#"IP range is invalid"
userInfo:nil];
#throw ipException;
}
}
}
#catch (NSException *exc) {
NSLog(#"ERROR: %#", [exc reason]);
}
You could modify the if conditional block to follow RFC 1918 guidelines, if you need that level of validation.

Swift edition:
func isIPAddressValid(ip: String) -> Bool {
guard let utf8Str = (ip as NSString).utf8String else {
return false
}
let utf8:UnsafePointer<Int8> = UnsafePointer(utf8Str)
var success: Int32
var dst: in_addr = in_addr()
success = inet_pton(AF_INET, utf8, &dst)
if (success != 1) {
var dst6: in6_addr? = in6_addr()
success = inet_pton(AF_INET6, utf8, &dst6);
}
return success == 1
}

A trick you can do is test the return of the inet_aton BSD call like this:
#include <arpa/inet.h>
- (BOOL)isIp:(NSString*)string{
struct in_addr pin;
int success = inet_aton([string UTF8String],&pin);
if (success == 1) return TRUE;
return FALSE;
}
Be aware however that this validates the string if it contains a ip address in any format, it is not restricted to the dotted format.

Related

Is it possible to use a GCD dispatch source to do an async connect() on a socket?

You can use GCD dispatch sources to read and write from sockets, monitor a listening socket for incoming connections, but I couldn't figure out how to also use a dispatch source to connect a socket?
In pseudo-code, it would look something like this:
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, connectingSocket, ...);
dispatch_source_set_event_handler(source, ^{
// Socket did connect or not
});
fcntl(connectingSocket, F_SETFL, O_NONBLOCK);
connect(connectingSocket, addr, len);
dispatch_source_resume(source);
This would be nicer than having to use select().
I initially mis-parsed your question... sorry. I get it now... you want to get EINPROGRESS from connect and have a dispatch source notify you when the connect call needs attention instead of polling with select... This was fairly easy to hack up, and appears to work:
#import <sys/types.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#implementation AppDelegate
{
dispatch_source_t foo;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
int socketFD = socket(PF_INET, SOCK_STREAM, 0);
if (socketFD == -1)
{
socketFD = -1;
abort();
}
int flags = fcntl(socketFD, F_GETFL, 0);
int status = fcntl(socketFD, F_SETFL, flags | O_NONBLOCK);
if (status == -1)
{
close(socketFD);
socketFD = -1;
abort();
}
struct sockaddr_in sockaddr4 = {0};
sockaddr4.sin_len = sizeof(sockaddr4);
sockaddr4.sin_family = AF_INET;
sockaddr4.sin_port = htons(22);
inet_aton("127.0.0.1", &sockaddr4.sin_addr);
foo = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socketFD, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(foo, ^{
if (connect(socketFD, (const struct sockaddr *)&sockaddr4, (socklen_t)sizeof(sockaddr4)))
{
int err = errno;
NSLog(#"errno: %s", strerror(err));
if (err == ECONNREFUSED)
{
abort();
}
else if (err == EISCONN)
{
// connected -- queue up other work
DoStuff();
// Cancel the source so it doesnt keep notifying...
dispatch_source_cancel(foo);
}
}
});
dispatch_source_set_cancel_handler(foo, ^{
NSLog(#"Cancel");
});
dispatch_resume(foo);
// Do initial connect
if (connect(socketFD, (const struct sockaddr *)&sockaddr4, (socklen_t)sizeof(sockaddr4)))
{
if(errno != EINPROGRESS)
{
close(socketFD);
socketFD = -1;
abort();
}
}
}
#end

Apportable gives compile error "arithmetic on a pointer to an incomplete type"

Apportable error
arithmetic on a pointer to an incomplete type 'struct if_msghdr'
socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
Here is the code. The code is working fine on ios/xcode but gives above error to interfaceMsgStruct with apportable. I have get this code from google and it is widely used so i dont think the code might have error. If it does then please correct me.
#import "MFMacAddress.h"
#implementation MFMacAddress
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
+ (NSString *)getMacAddress
{
int mgmtInfoBase[6];
char *msgBuffer = NULL;
size_t length;
unsigned char macAddress[6];
struct if_msghdr *interfaceMsgStruct;
struct sockaddr_dl *socketStruct;
NSString *errorFlag = NULL;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET; // Request network subsystem
mgmtInfoBase[1] = AF_ROUTE; // Routing table info
mgmtInfoBase[2] = 0;
mgmtInfoBase[3] = AF_LINK; // Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
// With all configured interfaces requested, get handle index
if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
errorFlag = #"if_nametoindex failure";
else
{
// Get the size of the data available (store in len)
if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
errorFlag = #"sysctl mgmtInfoBase failure";
else
{
// Alloc memory based on above call
if ((msgBuffer = malloc(length)) == NULL)
errorFlag = #"buffer allocation failure";
else
{
// Get system information, store in buffer
if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
errorFlag = #"sysctl msgBuffer failure";
}
}
}
// Befor going any further...
if (errorFlag != NULL)
{
NSLog(#"Error: %#", errorFlag);
return errorFlag;
}
// Map msgbuffer to interface message structure
interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
// Map to link-level socket structure
socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
// Copy link layer address data in socket structure to an array
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2],
macAddress[3], macAddress[4], macAddress[5]];
NSLog(#"Mac Address: %#", macAddressString);
// Release the buffer memory
free(msgBuffer);
return macAddressString;
}
#end
To get the mac address, you can do
[[UIDevice currentDevice] macAddress]
which returns an NSString *
The compile error you're seeing is the result of a different system headers between IOS and Android. We're working on a more transparent solution, but in the meantime the solution above is an easy workaround.
Often you get this error when the definition of struct if_msghdr is not 'visible'.
Check if you need to #include other header files, or if different conditional compilation options locked it out.

Objective-C: DNS TXT Record

I've been searching for a while without any success, how am I supposed to get a DNS TXT record for a domain in Objective-C?
My goal is to get the same output as: dig -t txt google.com +short
PS: No NSTask/pipping please! (I'm currently doing that but it's bad). The code can be written in C, I'll just write a wrapper later. I don't care about AppStore rules.
Thank you!
Use DNSServiceQueryRecord in dns_sd.h:
#import <dns_sd.h>
// ...
DNSServiceRef serviceRef;
DNSServiceQueryRecord(&serviceRef, 0, 0, "hmspl.de", kDNSServiceType_TXT,
kDNSServiceClass_IN, queryCallback, NULL);
DNSServiceProcessResult(serviceRef);
DNSServiceRefDeallocate(serviceRef);
// ...
static void queryCallback(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) {
if (errorCode == kDNSServiceErr_NoError && rdlen > 1) {
NSMutableData *txtData = [NSMutableData dataWithCapacity:rdlen];
for (uint16_t i = 1; i < rdlen; i += 256) {
[txtData appendBytes:rdata + i length:MIN(rdlen - i, 255)];
}
NSString *theTXT = [[NSString alloc] initWithBytes:txtData.bytes length:txtData.length encoding:NSASCIIStringEncoding];
NSLog(#"%#", theTXT);
}
}
See https://developer.apple.com/library/mac/#documentation/Networking/Reference/DNSServiceDiscovery_CRef/Reference/reference.html#//apple_ref/doc/c_ref/DNSServiceQueryRecord
can use like this code remove \x02 \x03, I removed \0x12 \0x17
use $man ascii check
char result[256]={0};
dn_expand(rdata, rdata + rdlen, rdata, result, 256);
NSMutableData *txtData = [NSMutableData dataWithCapacity:rdlen];
char *p=(char*)rdata;
for (uint16_t i = 0; i < rdlen; i++) {
DLog(#"%c\n",*p);
if (*p > 0x20) {
[txtData appendBytes:p length:1];
}
p++;
}

Find Mac OSX serial number

How to find the Mac OSX serial number.
Sometimes it is required to get serial number of a mac, and you validate on that.
I needed the same, few years back, when I developed a plugin for OsiriX. I was asked to release it in such a way, only few systems can use that plugin.
If we get any better solution than this, that will be quite helpful for all of us.
The following code is mainly copied from Technical Note TN1103,
with small modifications to return an NSString and to make it compile with ARC:
#include <IOKit/IOKitLib.h>
- (NSString *)getSerialNumber
{
NSString *serial = nil;
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert) {
CFTypeRef serialNumberAsCFString =
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault, 0);
if (serialNumberAsCFString) {
serial = CFBridgingRelease(serialNumberAsCFString);
}
IOObjectRelease(platformExpert);
}
return serial;
}
You have to add the IOKit.framework to your build settings.
This is the Swift version of the solution:
var serialNumber: String? {
let platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice") )
guard platformExpert > 0 else {
return nil
}
guard let serialNumber = (IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey as CFString, kCFAllocatorDefault, 0).takeUnretainedValue() as? String) else {
return nil
}
IOObjectRelease(platformExpert)
return serialNumber
}
This is a C++ version based on the TN1103 that Martin mention above.
C++ example:
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
std::string example_class::getSerialNumber()
{
CFStringRef serial;
char buffer[64] = {0};
std::string seriaNumber("");
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert)
{
CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault, 0);
if (serialNumberAsCFString) {
serial = (CFStringRef)serialNumberAsCFString;
}
if (CFStringGetCString(serial, buffer, 64, kCFStringEncodingUTF8)) {
seriaNumber = buffer;
}
IOObjectRelease(platformExpert);
}
return seriaNumber;
}

Help with Sending/ Receiving UDP packets - C Sockets

Ok, if you look at some of my previous questions, I've been working on getting a simple connection up and running with C sockets (I'm still fairly new to the whole networking aspect of an program, but everyone has to start somewhere, right?). I've included the code below that I have so far and when I execute it, I get no errors, but at the same time, I don't get the packet on the other end. By the way, I'm programming multicast sockets in objective-C and "msgStatus" is just a label in my GUI (it's hooked up correctly, so there's no problem there). I'm just not seeing where I'm going wrong. Could someone possibly help me or point me in the right direction? Thanks!
#define MAX_LEN 1024 /* maximum string size to send */
#define MIN_PORT 1024 /* minimum port allowed */
#define MAX_PORT 65535 /* maximum port allowed */
#define MYPORT 5673 /* port we will be using for our multicast socket */
-(void)broadcastMessage {//(NSString*)msg {
NSLog(#"broadcastMessage - Stage 1");
NSString *msg = #"From Master";
mc_ttl = 3; // number of node hops the message is allowed to travel across the network
// define the port we will be using
mc_port = MYPORT;
/* create a socket for sending to the multicast address */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
NSLog(#"ERROR: broadcastMessage - socket() failed");
return;
}
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr("225.0.0.37");
mc_addr.sin_port = htons(mc_port);
if (bind(sock, (struct sockaddr *) &mc_addr, sizeof(struct sockaddr_in)) < 0) {
NSLog(#"ERROR: bind not successful");
return;
}
NSLog(#"broadcastMessage - Stage 2");
/* set the TTL (time to live/hop count) for the send */
if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &mc_ttl, sizeof(mc_ttl))) < 0) {
NSLog(#"ERROR: broadcastMessage - setsockopt() failed");
return;
}
NSLog(#"broadcastMessage - Stage 3");
/* construct a multicast address structure - erase everything in the structure first*/
memset(&mc_addr, 0, sizeof(mc_addr));
// prepare the message to be sent
char send_str[MAX_LEN];
/* clear send buffer */
memset(send_str, 0, sizeof(send_str));
// convert the message to a C string to send
[msg getCString:send_str maxLength:MAX_LEN encoding:NSASCIIStringEncoding];
//while (fgets(send_str, MAX_LEN, stdin)) {
NSLog(#"broadcastMessage - Stage 4");
// send_len = strlen(send_str);
/* send string to multicast address */
if ((sendto(sock, send_str, sizeof(send_str), 0, (struct sockaddr *) &mc_addr, sizeof(mc_addr))) != sizeof(send_str)) {
NSLog(#"ERROR: broadcastMessage - sendto() sent incorrect number of bytes");
return;
}
NSLog(#"broadcastMessage - Stage 5");
/* clear send buffer */
memset(send_str, 0, sizeof(send_str));
NSLog(#"broadcastMessage - Stage 6");
close(sock);
}
-(void)listenForPackets {
listeningFlag_on = 1;
NSLog(#"listenForPackets - Stage 1");
if ((listeningSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
NSLog(#"ERROR: listenForPackets - socket() failed");
return; // make the method return an int instead of void and use this statement to check for errors
}
// set reuse port to on to allow multiple binds per host
if ((setsockopt(listeningSock, SOL_SOCKET, SO_REUSEADDR, &listeningFlag_on, sizeof(listeningFlag_on))) < 0) {
NSLog(#"ERROR: listenForPackets - setsockopt() failed");
return; // make the method return an int instead of void and use this statement to check for errors
}
NSLog(#"listenForPackets - Stage 2");
// construct a multicast address structure after erasing anything in the listeningmc_addr structure
memset(&listeningmc_addr, 0, sizeof(listeningmc_addr));
listeningmc_addr.sin_family = AF_INET;
listeningmc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
listeningmc_addr.sin_port = htons(mc_port);
// bind multicast address to socket
if ((bind(listeningSock, (struct sockaddr *) &listeningmc_addr, sizeof(listeningmc_addr))) < 0) {
NSLog(#"ERROR: listenForPackets - bind() failed");
return; // make the method return an int instead of void and use this statement to check for errors
}
//******************************************************************************************************************************
//******************************************************************************************************************************
NSString *ipAddress = [[NSString alloc] initWithString:self.getIPAddress];
const char *tmp = [ipAddress UTF8String];
listeningMc_addr_str = tmp;
printf("%s\n", listeningMc_addr_str);
listeningMc_req.imr_multiaddr.s_addr = inet_addr("225.0.0.37");
listeningMc_req.imr_interface.s_addr = htonl(INADDR_ANY);
// send an ADD MEMBERSHIP message via setsockopt
if ((setsockopt(listeningSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &listeningMc_req, sizeof(listeningMc_req))) < 0) {
NSLog(#"ERROR: listenForPackets - setsockopt() failed");
int err = errno;
NSLog(#"errno - %i", err);
NSLog(#"Error = %s", strerror(err));
perror("ERROR");
return; // make the method return an int instead of void and use this statement to check for errors
}
NSLog(#"listenForPackets - Stage 3");
for (;;) { // loop forever
// clear the receive buffers & structs
memset(listeningRecv_str, 0, sizeof(listeningRecv_str));
listeningFrom_len = sizeof(listeningFrom_addr);
memset(&listeningFrom_addr, 0, listeningFrom_len);
// block waiting to receive a packet
if ((listeningRecv_len = recvfrom(listeningSock, listeningRecv_str, MAX_LEN, 0, (struct sockaddr*)&listeningFrom_addr, &listeningFrom_len)) < 0) {
NSLog(#"ERROR: listenForPackets - recvfrom() failed");
return; // make the method return an int instead of void and use this statement to check for errors
}
NSLog(#"listenForPackets - Stage 4");
NSString *tmpy = [[NSString alloc] initWithCString:listeningRecv_str encoding:NSASCIIStringEncoding];
msgStatus.text = tmpy;
NSLog(#"ERROR");
}
// received string
printf("Received %d bytes from %s: ", listeningRecv_len, inet_ntoa(listeningFrom_addr.sin_addr));
printf("%s", listeningRecv_str);
}
// send a DROP MEMBERSHIP message via setsockopt
if ((setsockopt(listeningSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*) &listeningMc_req, sizeof(listeningMc_req))) < 0) {
NSLog(#"ERROR: listenForPackets - setsockopt() failed");
//return 1; // make the method return an int instead of void and use this statement to check for errors
}
close(listeningSock);
NSLog(#"listenForPackets - Stage 5 - Complete");
}
Here is the code I'm using to extract my IP Address.
-(NSString *)getIPAddress {
NSString *address = #"error";
struct ifaddrs *interfaces; // = NULL;
struct ifaddrs *temp_addr; // = NULL;
int success = 0;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0)
{
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL)
{
if(temp_addr->ifa_addr->sa_family == AF_INET)
{
// Check if interface is en0 which is the wifi connection on the iPhone
if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:#"en0"])
{
// Get NSString from C String
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return address;
}
In the listener, I think you need to set
listeningMc_req.imr_interface.s_addr = htonl(INADDR_ANY);
... since that is also the interface on which you bind the socket. Depending on whether you run everything on a single host, you might need to consider the loopback interface and binding to INADDR_ANY will do that.
Is there a router between you and your destination? If so, there's some work that needs to be done to tell the router that you want to subscribe to the feed as well as tell the router you will be sending the feed.
I would start by tcpdump-ing the connection to make sure the packet is leaving your machine first.