NSNetService Resolve ipv6 that can not make an http request on it - ios

Recently, i was facing a problem while resolving NSNetService.
I was published an NSNetService with type _http._tcp., to be just like an http server.
Other wise, on another device, i was start searching for this service, and it will find it.
After finding it, i was apply resolveWithTimeout on it.
While resolving, sometimes i was get only ipv6, that i can't make an HTTPRequest using NSURLConnection sendAsynchronousRequest: queue: completionHandler: on it.
How can i apply and HTTPRequest on url contains ipv6 ?
How can i deal with that problem ?

It looks like you're building a string like http://{IP}:{port}/ based on the information provided in NSNetService. And it works fine for an IPv4 address, the resulting string is like http://192.168.1.8:8080/.
However, IPv6 addresses use colons as a separator (instead of periods), so the same code generates a string like http://fe80::e31:db5a:0089:98ba:8080/, and the resulting address is incorrect. First, you need to wrap the address in square brackets: http://[fe80::e31:db5a:0089:98ba]:8080/. Second, fe80::/64 (as in the example) addresses are link-local and can be assigned to each IPv6-supporting interface, so you need to also provide the interface to use, e.g. http://[fe80::e31:db5a:0089:98ba%25en0]:8080/ where %25 is an encoded percent symbol and en0 is the interface name to use.
To sum up, you need to build different strings for IPv4 and IPv6 addresses. Speaking of which, there are Apple's recommendations:
As a rule, you should not resolve a service to an IP address and port number unless you are doing something very unusual.
– Connecting to a Bonjour Service by IP Address
Try to use this URL string if possible: http://{hostname}:{port}/, you won't need those extra IP address manipulations.

So this is what I end up using... it correctly translates address into URL including the interface:
NSString * result = nil;
char host[NI_MAXHOST];
char service[NI_MAXSERV];
int err;
err = getnameinfo(address.bytes, (socklen_t) address.length, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV);
if (err == 0) {
struct sockaddr_storage *sockaddr = (struct sockaddr_storage *)address.bytes;
if (sockaddr->ss_family == AF_INET6) {
result = [NSString stringWithFormat:#"[%s]:%s", host, service];
} else if (sockaddr->ss_family == AF_INET) {
result = [NSString stringWithFormat:#"%s:%s", host, service];
}
}

Related

Contiki: Get Global addresses from Local link addresses

In the file rpl-icmp6.c, in the method dio_input, the from variable is a local link address. I want to find the routes from the node who sent that DIO, using uip_ds6_route_lookup(&from). However, inside this method, they compare ip-addresses using uip_ipaddr_prefixcmp instead of uip_ipaddr_cmp. Therefore, I am wondering if there is a method in contiki that translate link-local addresses to prefix - global addresses of neighbor nodes?
EDIT:
uip_ipaddr_t from;
uip_ipaddr_copy(&from, &UIP_IP_BUF->srcipaddr);
route = uip_ds6_route_lookup(from);
if(route != NULL) {
const uip_ipaddr_t *nexthop = uip_ds6_route_nexthop(route);
if(uip_ipaddr_cmp(&from, &nexthop)) {
/* Test rank of the node */
return 1;
}
This is the code, and the method uip_ds6_route_lookup can not find any route because the ipaddress is local link.

In golang How to split a URL and encode back to URL from decoded components. Any packages?

In golang How to split a URL and encode back to URL from decoded components. Any packages that do the job?
net/url helps only in decoding the URL. I want to modify the HOST and PORT and recreate the URL. My problem originates from the case where I receive IPV6:port without square brackets. Let us say I get IPV6:port in the format as:
aaa:abbb:cccc:dddd:0000:0000:00aa:bbbb:8080/static/silly.html
I want to reconstruct the URL with brackets arround IPV6 address.
I think that would not be possible. For instance, if you get:
2001:db8::1:80
How could you tell if the IP address is
2001:db8::1
Or:
2001:db8::1:80
That is the reason why RFC 5952 recommands to use brackets (or some other characters) to distinguish the IP address from the port number.
Consequently, if possible, I recommand you to ignore this ambiguous formatting.
UPDATE
Actually, you can do what you want if you are sure to be able to distinguish the two parts, which is when you can count exatcly 8 occurences of the character :.
if strings.Count(url, ":") == 8 {
split := strings.Split(url, ":")
url = "[" + strings.Join(split[:8], ":") + "]:" + split[8]
}
But this is probably not the best idea to process this kind of url formatting anyway...
You can use the standard net package to help in the process [Playground][http://play.golang.org/p/wz0g7rgdU4]
I use the net.ParseIP and net.JoinHostPort
package main
import (
"fmt"
"net"
"strings"
)
func splitLastColumn(host string) (string, string, string) {
idx := strings.Index(host, "/")
col_idx := strings.LastIndex(host[:idx], ":")
return host[:col_idx], host[col_idx+1 : idx], host[idx:]
}
func main() {
ipstr := "aaa:abbb:cccc:dddd:0000:0000:00aa:bbbb:8080/static/silly.html"
host, port, path := splitLastColumn(ipstr)
ip := net.ParseIP(host)
if ip == nil {
fmt.Println("invalid addr ")
}
fmt.Println(ip, host, port, path)
fmt.Println(net.JoinHostPort(host, port))
}

recvfrom() get the wrong source address

when i recvfrom(), the received message is correct, but the source address is totally a mess,
why is that happening?
char traid_messageR[MAXDATASIZE];
socklen_t addlen;
struct sockaddr_in source_addr;
if((numbytes=recvfrom(udp_sockfd, traid_messageR, 256, 0, (struct sockaddr*)&source_addr, &addlen)) == -1)
{
perror("recvfrom");
exit(1);
}
the result is like this:
(gdb) print source_addr
$1 = {sin_family = 61428, sin_port = 42, sin_addr = {s_addr = 49809},
sin_zero = "\234\352\377\277\310\352\377\277"}
the 49809 looks like a port number, but it is the port number of this receiver...does any one have idea why is this?thanks a lot
oh, another thing, i used this in a select() loop, IF_ISSET(und_socked,%fds),then exceute the above code, does this affect?
you didn't assign value to addlen
addlen = sizeof(source_addr)
UPDATE: refer to http://pubs.opengroup.org/onlinepubs/7908799/xns/recvfrom.html
The manual says
address_len Specifies the length of the sockaddr structure pointed
to
by the address argument. .....
If the address argument is not a null pointer and the protocol provides the source address of messages, the source address of the
received message is stored in the sockaddr structure pointed to by the
address argument, and the length of this address is stored in the
object pointed to by the address_len argument.
I found it explained better here:
In this case, addrlen is a value-
result argument. Before the call, it should be initialized to the
size of the buffer associated with src_addr. Upon return, addrlen is
updated to contain the actual size of the source address.
http://man7.org/linux/man-pages/man2/recv.2.html

Default DNS server in monotouch

I'm wondering how do I get default DNS server in monotouch?
this code works perfectly in simulator, but gives 0 records on device.
NetworkInterface.GetAllNetworkInterfaces();
foreach (IPAddress ipAddr in ipProps.DnsAddresses)
Console.WriteLine(ipAddr);
from the other hand, this code works on both simulator and device:
IPHostEntry he = Dns.GetHostEntry(domain);
dns = he.HostName.ToString();
having all this, I assume DNS server address is stored somewhere. I mean it is accessible. How to get its IP?
This will get the IP Address in MonoTouch:-
public string GetIPAddress()
{
string address = "Not Connected";
try
{
#if SIM
address = IPAddress.FileStyleUriParser("127.0.0.1");
#else
string str = Dns.GetHostName() + ".local";
IPHostEntry hostEntry = Dns.GetHostEntry(str);
address = (
from addr in hostEntry.AddressList
where addr.AddressFamily == AddressFamily.InterNetwork
select addr.ToString()
).FirstOrDefault();
#endif
}
catch (Exception ex)
{
// Add error handling....
}
return address;
}
Note the difference between using the simulator and device.
I do not believe such an API exists on iOS (but I would be happy to be proven wrong). Other projects, that needs this information, relies on hacks like using well known, static address to DNS servers) to overcome this.
Now the reason code like this:
var all = NetworkInterface.GetAllNetworkInterfaces ();
foreach (NetworkInterface ni in all) {
var props = ni.GetIPProperties ();
foreach (var dns in props.DnsAddresses) {
Console.WriteLine (dns);
}
}
works on the simulator is because it's a simulator and not an emulator. IOW the host (Mac) computer allows far more things than a real iOS device will allow.
More precisely props will be an instance of System.Net.NetworkInformation.MacOsIPInterfaceProperties, which inherits from UnixIPInterfaceProperties, and ends up reading the /etc/resolv.conf file (which iOS disallow your application from reading).
The second case, calling Dns.GetHostEntry, goes down into the Mono runtime but end up calling gethostname which does not require the caller to know the DNS server address.

Extending packet header from within a Netfilter hook

I want to prepend IP header on an existing IP packet while inside NF_HOOK_LOCAL_OUT. The issue I face is that the skb expansion functions (such as copy/clone/expand/reallocate header) allocate a new sk_buff. We can not return this newly allocated pointer since netfilter hook function no longer (kernel version 2.6.31) passes the skb pointer's address (passes by value). How I solved the issue is as follows:
1. I got a new skb using skb_header_realloc(). This copies all the data from skb.
2. I modified the new skb (call it skb2) to prepend the new IP header, set appropriate values in the new IP header.
3. Replace the contents of the original skb (passed in the Netfilter hook function) with the contents of the skb2 using skb_morph(). Returned NF_ACCEPT.
Is this the only way of achieving what I intended to? Is there a more efficient solution? Are there other use cases of skb_morph (besides the IP reassembly code)?
This works for me, in 2.6 kernels:
...
struct iphdr* iph;
if (skb_headroom(skb) < sizeof(struct iphdr))
if (0 != pskb_expand_head(skb, sizeof(struct iphdr) - skb_headroom(skb), 0, GFP_ATOMIC)) {
printk("YOUR FAVOURITE ERROR MESSAGE");
kfree_skb(skb);
return NF_STOLEN;
}
iph = (struct iphdr*) skb_push(skb, sizeof(struct iphdr));
//Fill ip packet
return NF_ACCEPT;
Hope it helps.

Resources