]> git.sur5r.net Git - i3/i3status/blob - src/print_ipv6_addr.c
fix: use SYSCONFDIR in error message
[i3/i3status] / src / print_ipv6_addr.c
1 // vim:ts=4:sw=4:expandtab
2 #include <config.h>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7
8 #include <stdio.h>
9 #include <stdbool.h>
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <netdb.h>
13 #include <string.h>
14 #include <yajl/yajl_gen.h>
15 #include <yajl/yajl_version.h>
16
17 #include "i3status.h"
18
19 static char *get_sockname(struct addrinfo *addr) {
20     static char buf[INET6_ADDRSTRLEN + 1];
21     struct sockaddr_storage local;
22     int ret;
23     int fd;
24
25     if ((fd = socket(addr->ai_family, SOCK_DGRAM, 0)) == -1) {
26         perror("socket()");
27         return NULL;
28     }
29
30     /* Since the socket was created with SOCK_DGRAM, this is
31      * actually not establishing a connection or generating
32      * any other network traffic. Instead, as a side-effect,
33      * it saves the local address with which packets would
34      * be sent to the destination. */
35     if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) {
36         /* We don’t display the error here because most
37          * likely, there just is no IPv6 connectivity.
38          * Thus, don’t spam the user’s console but just
39          * try the next address. */
40         (void)close(fd);
41         return NULL;
42     }
43
44     socklen_t local_len = sizeof(struct sockaddr_storage);
45     if (getsockname(fd, (struct sockaddr *)&local, &local_len) == -1) {
46         perror("getsockname()");
47         (void)close(fd);
48         return NULL;
49     }
50
51     memset(buf, 0, INET6_ADDRSTRLEN + 1);
52     if ((ret = getnameinfo((struct sockaddr *)&local, local_len,
53                            buf, sizeof(buf), NULL, 0,
54                            NI_NUMERICHOST)) != 0) {
55         fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret));
56         (void)close(fd);
57         return NULL;
58     }
59
60     (void)close(fd);
61     return buf;
62 }
63
64 /*
65  * Returns the IPv6 address with which you have connectivity at the moment.
66  * The char * is statically allocated and mustn't be freed
67  */
68 static char *get_ipv6_addr(void) {
69     struct addrinfo hints;
70     struct addrinfo *result, *resp;
71     static struct addrinfo *cached = NULL;
72
73     /* To save dns lookups (if they are not cached locally) and creating
74      * sockets, we save the fd and keep it open. */
75     if (cached != NULL)
76         return get_sockname(cached);
77
78     memset(&hints, 0, sizeof(struct addrinfo));
79     hints.ai_family = AF_INET6;
80     hints.ai_socktype = SOCK_DGRAM;
81
82     /* We use the public IPv6 of the K root server here. It doesn’t matter
83      * which IPv6 address we use (we don’t even send any packets), as long
84      * as it’s considered global by the kernel.
85      * NB: We don’t use a hostname since that would trigger a DNS lookup.
86      * By using an IPv6 address, getaddrinfo() will *not* do a DNS lookup,
87      * but return the address in the appropriate struct. */
88     if (getaddrinfo("2001:7fd::1", "domain", &hints, &result) != 0) {
89         /* We don’t display the error here because most
90          * likely, there just is no connectivity.
91          * Thus, don’t spam the user’s console. */
92         return NULL;
93     }
94
95     for (resp = result; resp != NULL; resp = resp->ai_next) {
96         char *addr_string = get_sockname(resp);
97         /* If we could not get our own address and there is more than
98          * one result for resolving k.root-servers.net, we cannot
99          * cache. Otherwise, no matter if we got IPv6 connectivity or
100          * not, we will cache the (single) result and are done. */
101         if (!addr_string && result->ai_next != NULL)
102             continue;
103
104         if ((cached = malloc(sizeof(struct addrinfo))) == NULL)
105             return NULL;
106         memcpy(cached, resp, sizeof(struct addrinfo));
107         if ((cached->ai_addr = malloc(resp->ai_addrlen)) == NULL) {
108             cached = NULL;
109             return NULL;
110         }
111         memcpy(cached->ai_addr, resp->ai_addr, resp->ai_addrlen);
112         freeaddrinfo(result);
113         return addr_string;
114     }
115
116     freeaddrinfo(result);
117     return NULL;
118 }
119
120 void print_ipv6_info(yajl_gen json_gen, char *buffer, const char *format_up, const char *format_down) {
121     const char *walk;
122     char *addr_string = get_ipv6_addr();
123     char *outwalk = buffer;
124
125     if (addr_string == NULL) {
126         START_COLOR("color_bad");
127         outwalk += sprintf(outwalk, "%s", format_down);
128         END_COLOR;
129         OUTPUT_FULL_TEXT(buffer);
130         return;
131     }
132
133     START_COLOR("color_good");
134     for (walk = format_up; *walk != '\0'; walk++) {
135         if (*walk != '%') {
136             *(outwalk++) = *walk;
137
138         } else if (BEGINS_WITH(walk + 1, "ip")) {
139             outwalk += sprintf(outwalk, "%s", addr_string);
140             walk += strlen("ip");
141
142         } else {
143             *(outwalk++) = '%';
144         }
145     }
146     END_COLOR;
147     OUTPUT_FULL_TEXT(buffer);
148 }