#include <errno.h>
#include <ifaddrs.h>
#include <jansson.h>
#include <netdb.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#define fatal(msg) \
fprintf(stderr, "%s\n", msg); \
abort();
// must effectively crashes the program if allocation of memory in jansson
// library fails.
static json_t* must(json_t* p) {
if (!p) {
fatal("failed to allocate json object");
}
return p;
}
static void usage(const char* prog) {
fprintf(stderr, "usage: %s [interface name]\n", prog);
exit(2);
}
int main(int argc, char** argv) {
if ((argc > 2) || ((argc > 1) && (strcasecmp(argv[1], "-h") == 0))) {
usage(argv[0]);
}
struct ifaddrs* addresses;
bool supplied_name = false;
char* find_iface_name = NULL;
if (argc > 1) {
supplied_name = true;
find_iface_name = argv[1];
}
if (getifaddrs(&addresses) == -1) {
const char* gai_err = gai_strerror(errno);
fprintf(stderr, "{\"error\": \"%s\"}", gai_err);
return EXIT_FAILURE;
}
json_t* top_level_obj = must(json_object());
struct ifaddrs* address = addresses;
while (address) {
if (address->ifa_addr == NULL) {
address = address->ifa_next;
continue;
}
// Skip to next address if there is not a match
if ((supplied_name) && (strcasecmp(find_iface_name, address->ifa_name))) {
address = address->ifa_next;
continue;
};
int family = address->ifa_addr->sa_family;
char ap[NI_MAXHOST];
char nmp[NI_MAXHOST];
if (family == AF_INET || family == AF_INET6) {
const int family_size = family == AF_INET ? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6);
getnameinfo(address->ifa_addr, family_size, ap, NI_MAXHOST, NULL, 0,
NI_NUMERICHOST);
getnameinfo(address->ifa_netmask, family_size, nmp, NI_MAXHOST, NULL, 0,
NI_NUMERICHOST);
json_t* j = must(json_object());
json_t* ifa_name = must(json_string(address->ifa_name));
json_object_set_new(j, "name", ifa_name);
json_t* ifa_family = must(json_string(family == AF_INET ? "v4" : "v6"));
json_object_set_new(j, "family", ifa_family);
json_t* ifa_address = must(json_string(ap));
json_object_set_new(j, "address", ifa_address);
json_t* ifa_netmask = must(json_string(nmp));
json_object_set_new(j, "netmask", ifa_netmask);
json_t* this_interface =
json_object_get(top_level_obj, address->ifa_name);
if (!this_interface) {
this_interface = must(json_object());
json_t* packed =
must(json_pack("{s{ssss}}", family == AF_INET ? "v4" : "v6",
"address", ap, "netmask", nmp));
int res = json_object_set(top_level_obj, address->ifa_name, packed);
if (res == -1) {
json_t* err =
must(json_pack("{ss}", "error", "json_object_set() failed"));
fprintf(stderr, "%s\n", json_dumps(err, JSON_COMPACT));
exit(EXIT_FAILURE);
}
} else {
json_t* packed =
must(json_pack("{s{ssss}}", family == AF_INET ? "v4" : "v6",
"address", ap, "netmask", nmp));
int res = json_object_update_missing(this_interface, packed);
if (res == -1) {
json_t* err = must(json_pack("{ss}", "error",
"json_object_update_missing() failed"));
fprintf(stderr, "%s\n", json_dumps(err, JSON_COMPACT));
exit(EXIT_FAILURE);
}
}
}
address = address->ifa_next;
}
char* buf = json_dumps(top_level_obj, JSON_COMPACT);
if (!buf) {
json_t* err = must(json_pack("{ss}", "error", "json_dumps() failed"));
fprintf(stderr, "%s\n", json_dumps(err, JSON_COMPACT));
exit(EXIT_FAILURE);
}
printf("%s\n", buf);
free(buf);
freeifaddrs(addresses);
return EXIT_SUCCESS;
}