QUOTE: Let your heart guide you always.

dns

Minimal dynamic DNS updater

main.c (6120B)


      1#include <stdio.h>
      2#include <stdlib.h>
      3#include <string.h>
      4#include <curl/curl.h>
      5#define LIB_STR_IMPL
      6#include <libstr.h>
      7
      8#define DNS_ENV_UPDATE  "DNS_UPDATE_URL"
      9#define DNS_ENV_LOOKUP  "DNS_LOOKUP_URL"
     10#define DNS_ENV_RESOLVE "DNS_RESOLVE_URL"
     11
     12#define DNS_SYMBOL_HOST    "HOST"
     13#define DNS_SYMBOL_DOMAIN  "DOMAIN"
     14#define DNS_SYMBOL_IP_ADDR "IP_ADDR"
     15#define DNS_SYMBOL_PASSWD  "PASSWD"
     16
     17#define DNS_DOMAIN_WILDCARD "@"
     18
     19typedef enum {
     20  DNS_ERR_OK,
     21  DNS_ERR_FAIL,
     22} dns_error_t;
     23
     24typedef struct {
     25  char *host;
     26  char *domain;
     27  char *passwd;
     28} dns_domain_t;
     29
     30size_t dns_curl_write(void *ptr, size_t size, size_t nmemb, void *data) {
     31  size_t len = size * nmemb;
     32  char **curl_data = (char **) data;
     33  str_append_len(curl_data, ptr, len);
     34  return len;
     35}
     36
     37dns_error_t dns_update(char *url, dns_domain_t *domain, char *ip_addr) {
     38  CURL *curl = curl_easy_init();
     39  if (!curl) {
     40    fprintf(stderr, "CURL init failed!\n");
     41    return DNS_ERR_FAIL;
     42  }
     43  char *resolved_url = url;
     44  resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_HOST "}", domain->host);
     45  resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_DOMAIN "}", domain->domain);
     46  resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_PASSWD "}", domain->passwd);
     47  resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_IP_ADDR "}", ip_addr);
     48  char *curl_data = NULL;
     49  curl_easy_setopt(curl, CURLOPT_URL, resolved_url);
     50  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dns_curl_write);
     51  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
     52  CURLcode code = curl_easy_perform(curl);
     53  free(resolved_url);
     54  if (code != CURLE_OK) {
     55    curl_easy_cleanup(curl);
     56    fprintf(stderr, "CURL fetch failed!\n%s\n", curl_easy_strerror(code));
     57    return DNS_ERR_FAIL;
     58  }
     59  curl_easy_cleanup(curl);
     60  for (size_t i = 0; i < strlen(curl_data); i++) {
     61    if (strncmp(curl_data + i, "<ErrCount>0</ErrCount>", 22) == 0) {
     62      free(curl_data);
     63      return DNS_ERR_OK;
     64    }
     65  }
     66  fprintf(stderr, "DNS update error:\n%s\n", curl_data);
     67  if (curl_data != NULL) {
     68    free(curl_data);
     69  }
     70  return DNS_ERR_FAIL;
     71}
     72
     73dns_error_t dns_lookup(char *url, dns_domain_t *domain, char **ip_addr) {
     74  CURL *curl = curl_easy_init();
     75  if (!curl) {
     76    fprintf(stderr, "CURL init failed!\n");
     77    return DNS_ERR_FAIL;
     78  }
     79  char *resolved_url = url;
     80  resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_HOST "}", domain->host);
     81  resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_DOMAIN "}", domain->domain);
     82  char *curl_data = NULL;
     83  curl_easy_setopt(curl, CURLOPT_URL, resolved_url);
     84  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dns_curl_write);
     85  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
     86  CURLcode code = curl_easy_perform(curl);
     87  free(resolved_url);
     88  if (code != CURLE_OK) {
     89    curl_easy_cleanup(curl);
     90    fprintf(stderr, "CURL fetch failed!\n%s\n", curl_easy_strerror(code));
     91    return DNS_ERR_FAIL;
     92  }
     93  curl_easy_cleanup(curl);
     94  size_t set = 0;
     95  for (size_t i = 0; i < strlen(curl_data);) {
     96    if (strncmp(curl_data + i, "\"data\":\"", 8) == 0) {
     97      i += 8;
     98      set = i;
     99      continue;
    100    }
    101    if (set > 0 && curl_data[i] == '"') {
    102      str_append_len(ip_addr, curl_data + set, i - set);
    103      break;
    104    }
    105    i++;
    106  }
    107  if (curl_data != NULL) {
    108    free(curl_data);
    109  }
    110  return DNS_ERR_OK;
    111}
    112
    113dns_error_t dns_resolve(char *url, char **ip_addr) {
    114  CURL *curl = curl_easy_init();
    115  if (!curl) {
    116    fprintf(stderr, "CURL init failed!\n");
    117    return DNS_ERR_FAIL;
    118  }
    119  char *curl_data = NULL;
    120  curl_easy_setopt(curl, CURLOPT_URL, url);
    121  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dns_curl_write);
    122  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
    123  CURLcode code = curl_easy_perform(curl);
    124  if (code != CURLE_OK) {
    125    curl_easy_cleanup(curl);
    126    fprintf(stderr, "CURL fetch failed!\n%s\n", curl_easy_strerror(code));
    127    return DNS_ERR_FAIL;
    128  }
    129  curl_easy_cleanup(curl);
    130  for (size_t i = 0; i < strlen(curl_data); i++) {
    131    if (curl_data[i] == '\n') {
    132      str_append_len(ip_addr, curl_data, i);
    133      break;
    134    }
    135  }
    136  if (curl_data != NULL) {
    137    free(curl_data);
    138  }
    139  return DNS_ERR_OK;
    140}
    141
    142int main(int argc, char **argv) {
    143  if (argc != 4) {
    144    fprintf(stderr, "Invalid arguments!\nUsage: dns <host> <domain> <passwd>\n");
    145    return EXIT_FAILURE;
    146  }
    147  dns_domain_t domain = {
    148    .host = argv[1],
    149    .domain = argv[2],
    150    .passwd = argv[3],
    151  };
    152  if (strcmp(domain.host, DNS_DOMAIN_WILDCARD) == 0) {
    153    fprintf(stdout, "DNS domain: %s\n", domain.domain);
    154  }
    155  else {
    156    fprintf(stdout, "DNS domain: %s.%s\n", domain.host, domain.domain);
    157  }
    158  char *dns_update_url = getenv(DNS_ENV_UPDATE);
    159  if (dns_update_url == NULL) {
    160    fprintf(stderr, "Environment variable missing!\nDefine: " DNS_ENV_UPDATE "\n");
    161    return EXIT_FAILURE;
    162  }
    163  char *dns_lookup_url = getenv(DNS_ENV_LOOKUP);
    164  if (dns_lookup_url == NULL) {
    165    fprintf(stderr, "Environment variable missing!\nDefine: " DNS_ENV_LOOKUP "\n");
    166    return EXIT_FAILURE;
    167  }
    168  char *dns_resolve_url = getenv(DNS_ENV_RESOLVE);
    169  if (dns_resolve_url == NULL) {
    170    fprintf(stderr, "Environment variable missing!\nDefine: " DNS_ENV_RESOLVE "\n");
    171    return EXIT_FAILURE;
    172  }
    173  char *ip_addr = NULL;
    174  if (dns_resolve(dns_resolve_url, &ip_addr) != DNS_ERR_OK) {
    175    fprintf(stderr, "IP address resolution failed!\n");
    176    return EXIT_FAILURE;
    177  }
    178  fprintf(stdout, "DNS resolve IP_ADDR: %s\n", ip_addr);
    179  char *domain_ip_addr = NULL;
    180  if (dns_lookup(dns_lookup_url, &domain, &domain_ip_addr) != DNS_ERR_OK) {
    181    fprintf(stderr, "DNS lookup failed!\n");
    182    return EXIT_FAILURE;
    183  }
    184  fprintf(stdout, "DNS lookup DOMAIN_IP_ADDR: %s\n", domain_ip_addr);
    185  if (strcmp(ip_addr, domain_ip_addr) != 0) {
    186    if (dns_update(dns_update_url, &domain, ip_addr) != DNS_ERR_OK) {
    187      free(ip_addr);
    188      free(domain_ip_addr);
    189      fprintf(stderr, "DNS update FAILED!\n");
    190      return EXIT_FAILURE;
    191    }
    192    fprintf(stdout, "DNS update SUCCESSFUL.\n");
    193  }
    194  else {
    195    fprintf(stdout, "DNS domain already UP-TO-DATE.\n");
    196  }
    197  free(ip_addr);
    198  free(domain_ip_addr);
    199  return EXIT_SUCCESS;
    200}