Getaddrinfo sucks. everything else is much worse
valentin.gosu.se
valentin.gosu.se
This is the TXR Lisp interactive listener of TXR 298.
Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
1> (getaddrinfo "news.ycombinator.com")
(#S(sockaddr-in canonname nil addr 3520653007 port 0 prefix 32)
#S(sockaddr-in canonname nil addr 3520653007 port 0 prefix 32)
#S(sockaddr-in6 canonname nil addr 50544109533264435600708011529282256934 port 0
flow-info 0 scope-id 0 prefix 128)
#S(sockaddr-in6 canonname nil addr 50544109533264435600708011529282256934 port 0
flow-info 0 scope-id 0 prefix 128))
It would be tidy to deduplicate that, though it is overhead.Look at the classic struct sockaddr_in structure it has a member sin_addr of type struct in_addr. What is that?
In my header files here I have it as:
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
The C structure has an integer, so the Lisp analog does also.C socket code does things like
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
INADDR_LOOPBACK is 127.0.0.1 encoded as an integer.It's easy to to do bitwise arithmetic on it. E.g. to apply a netmask like 255.0.0.0, you literally just form the netmask as 0xFF000000, and use your & operator: addr & 0xFF00000000.
In the case of IPv6, there is a union: a view of the address as 16 bytes, 8 words, or 4 double words.
Since I have bignum integers, I just made it a simple integer, so it looks like the IPv4 case. You can do bitwise arithmetic on it in a straightforward.
We can get the addresses in text format like this:
1> (getaddrinfo "news.ycombinator.com")
(#S(sockaddr-in canonname nil addr 3520653007 port 0 prefix 32)
#S(sockaddr-in canonname nil addr 3520653007 port 0 prefix 32)
#S(sockaddr-in6 canonname nil addr 50544109533264435600708011529282256934 port 0
flow-info 0 scope-id 0 prefix 128)
#S(sockaddr-in6 canonname nil addr 50544109533264435600708011529282256934 port 0
flow-info 0 scope-id 0 prefix 128))
2> (first *1)
#S(sockaddr-in canonname nil addr 3520653007 port 0 prefix 32)
3> (third *1)
#S(sockaddr-in6 canonname nil addr 50544109533264435600708011529282256934 port 0
flow-info 0 scope-id 0 prefix 128)
4> (str-inaddr *2.addr)
"209.216.230.207"
5> (str-in6addr *3.addr)
"2606:7100:1:67::26"
str-inaddr and str-in6addr* take an integer and return a string.Their rough opposites return a structure, because the general text format can encode prefix and port number info.
6> (inaddr-str *4)
#S(sockaddr-in canonname nil addr 3520653007 port 0 prefix 32)
7> (in6addr-str *5)
#S(sockaddr-in6 canonname nil addr 50544109533264435600708011529282256934 port 0
flow-info 0 scope-id 0 prefix 128)
The structures also support a generic method str-addr: 8> (mapcar .(str-addr) (getaddrinfo "news.ycombinator.com"))
("209.216.230.207" "209.216.230.207" "2606:7100:1:67::26" "2606:7100:1:67::26")
This will take into account prefix and port number if they are present and nonzero.We could define a custom print method in the sockaddr base:
9> (defmeth sockaddr print (self stream pretty-p)
(put-string `#<@(typeof self) @{self.(str-addr)}>` stream))
(meth sockaddr
print)
10> (getaddrinfo "news.ycombinator.com")
(#<sockaddr-in 209.216.230.207> #<sockaddr-in 209.216.230.207>
#<sockaddr-in6 2606:7100:1:67::26> #<sockaddr-in6 2606:7100:1:67::26>)The only platform which has systemd resolver api already has a nice working function.
In the same way, it looks ridiculous to me that in the http3, Firefox will silently target a different port than the one that is requested and displayed...
So I kinda doubt we'll ever see that one fixed.