X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fprint_ipv6_addr.c;h=5a0c38d993d1fb59c003ab10725cf7d8789ad7e5;hb=0e4fd9ad4a4cc7d6c770134a077e91b02f6cd2f0;hp=fd02dba2ed2406b223fddd2ee76c284c0954adaa;hpb=37882cee889d556a932d26084119f516d6639b13;p=i3%2Fi3status diff --git a/src/print_ipv6_addr.c b/src/print_ipv6_addr.c index fd02dba..5a0c38d 100644 --- a/src/print_ipv6_addr.c +++ b/src/print_ipv6_addr.c @@ -1,111 +1,148 @@ -// vim:ts=8:expandtab +// vim:ts=4:sw=4:expandtab +#include +#include +#include +#include +#include + #include +#include #include #include -#include -#include #include #include -#include - -static void print_sockname(int fd) { - static char buf[INET6_ADDRSTRLEN+1]; - struct sockaddr_storage local; - int ret; - - socklen_t local_len = sizeof(struct sockaddr_storage); - if (getsockname(fd, (struct sockaddr*)&local, &local_len) == -1) { - perror("getsockname()"); - (void)close(fd); - printf("no IPv6"); - return; - } - - memset(buf, 0, INET6_ADDRSTRLEN + 1); - if ((ret = getnameinfo((struct sockaddr*)&local, local_len, - buf, sizeof(buf), NULL, 0, - NI_NUMERICHOST)) != 0) { - fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(ret)); - printf("no IPv6"); - return; - } - - printf("%s", buf); +#include +#include + +#include "i3status.h" + +static char *get_sockname(struct addrinfo *addr) { + static char buf[INET6_ADDRSTRLEN + 1]; + struct sockaddr_storage local; + int ret; + int fd; + + if ((fd = socket(addr->ai_family, SOCK_DGRAM, 0)) == -1) { + perror("socket()"); + return NULL; + } + + /* Since the socket was created with SOCK_DGRAM, this is + * actually not establishing a connection or generating + * any other network traffic. Instead, as a side-effect, + * it saves the local address with which packets would + * be sent to the destination. */ + if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) { + /* We don’t display the error here because most + * likely, there just is no IPv6 connectivity. + * Thus, don’t spam the user’s console but just + * try the next address. */ + (void)close(fd); + return NULL; + } + + socklen_t local_len = sizeof(struct sockaddr_storage); + if (getsockname(fd, (struct sockaddr *)&local, &local_len) == -1) { + perror("getsockname()"); + (void)close(fd); + return NULL; + } + + memset(buf, 0, INET6_ADDRSTRLEN + 1); + if ((ret = getnameinfo((struct sockaddr *)&local, local_len, + buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST)) != 0) { + fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret)); + (void)close(fd); + return NULL; + } + + (void)close(fd); + return buf; } /* * Returns the IPv6 address with which you have connectivity at the moment. - * + * The char * is statically allocated and mustn't be freed */ -static void print_ipv6_addr() { - struct addrinfo hints; - struct addrinfo *result, *resp; - static int fd = -1; - - /* To save dns lookups (if they are not cached locally) and creating - * sockets, we save the fd and keep it open. */ - if (fd > -1) { - print_sockname(fd); - return; - } - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET6; - - /* We resolve the K root server to get a public IPv6 address. You can - * replace this with any other host which has an AAAA record, but the - * K root server is a pretty safe bet. */ - if (getaddrinfo("k.root-servers.net", "domain", &hints, &result) != 0) { - /* We don’t display the error here because most - * likely, there just is no connectivity. - * Thus, don’t spam the user’s console. */ - printf("no IPv6"); - return; +static char *get_ipv6_addr(void) { + struct addrinfo hints; + struct addrinfo *result, *resp; + static struct addrinfo *cached = NULL; + + /* To save dns lookups (if they are not cached locally) and creating + * sockets, we save the fd and keep it open. */ + if (cached != NULL) + return get_sockname(cached); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + + /* We use the public IPv6 of the K root server here. It doesn’t matter + * which IPv6 address we use (we don’t even send any packets), as long + * as it’s considered global by the kernel. + * NB: We don’t use a hostname since that would trigger a DNS lookup. + * By using an IPv6 address, getaddrinfo() will *not* do a DNS lookup, + * but return the address in the appropriate struct. */ + if (getaddrinfo("2001:7fd::1", "domain", &hints, &result) != 0) { + /* We don’t display the error here because most + * likely, there just is no connectivity. + * Thus, don’t spam the user’s console. */ + return NULL; + } + + for (resp = result; resp != NULL; resp = resp->ai_next) { + char *addr_string = get_sockname(resp); + /* If we could not get our own address and there is more than + * one result for resolving k.root-servers.net, we cannot + * cache. Otherwise, no matter if we got IPv6 connectivity or + * not, we will cache the (single) result and are done. */ + if (!addr_string && result->ai_next != NULL) + continue; + + if ((cached = malloc(sizeof(struct addrinfo))) == NULL) + return NULL; + memcpy(cached, resp, sizeof(struct addrinfo)); + if ((cached->ai_addr = malloc(resp->ai_addrlen)) == NULL) { + cached = NULL; + return NULL; } + memcpy(cached->ai_addr, resp->ai_addr, resp->ai_addrlen); + freeaddrinfo(result); + return addr_string; + } - for (resp = result; resp != NULL; resp = resp->ai_next) { - if ((fd = socket(resp->ai_family, SOCK_DGRAM, 0)) == -1) { - perror("socket()"); - continue; - } - - /* Since the socket was created with SOCK_DGRAM, this is - * actually not establishing a connection or generating - * any other network traffic. Instead, as a side-effect, - * it saves the local address with which packets would - * be sent to the destination. */ - if (connect(fd, resp->ai_addr, resp->ai_addrlen) == -1) { - /* We don’t display the error here because most - * likely, there just is no IPv6 connectivity. - * Thus, don’t spam the user’s console but just - * try the next address. */ - (void)close(fd); - continue; - } - - free(result); - - print_sockname(fd); - - return; - } - - free(result); - printf("no IPv6"); + freeaddrinfo(result); + return NULL; } -void print_ipv6_info(const char *format) { - const char *walk; - - for (walk = format; *walk != '\0'; walk++) { - if (*walk != '%') { - putchar(*walk); - continue; - } - - if (strncmp(walk+1, "ip", strlen("ip")) == 0) { - print_ipv6_addr(); - walk += strlen("ip"); - } +void print_ipv6_info(yajl_gen json_gen, char *buffer, const char *format_up, const char *format_down) { + const char *walk; + char *addr_string = get_ipv6_addr(); + char *outwalk = buffer; + + if (addr_string == NULL) { + START_COLOR("color_bad"); + outwalk += sprintf(outwalk, "%s", format_down); + END_COLOR; + OUTPUT_FULL_TEXT(buffer); + return; + } + + START_COLOR("color_good"); + for (walk = format_up; *walk != '\0'; walk++) { + if (*walk != '%') { + *(outwalk++) = *walk; + + } else if (BEGINS_WITH(walk + 1, "ip")) { + outwalk += sprintf(outwalk, "%s", addr_string); + walk += strlen("ip"); + + } else { + *(outwalk++) = '%'; } + } + END_COLOR; + OUTPUT_FULL_TEXT(buffer); }