QUOTE: Love yourself first, then others.

main.c - dns - Minimal dynamic DNS updater

dns

Minimal dynamic DNS updater
git clone git@soophie.de:/srv/git/dns
log | files | refs | readme

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 
     19 typedef enum {
     20   DNS_ERR_OK,
     21   DNS_ERR_FAIL,
     22 } dns_error_t;
     23 
     24 typedef struct {
     25   char *host;
     26   char *domain;
     27   char *passwd;
     28 } dns_domain_t;
     29 
     30 size_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 
     37 dns_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 
     73 dns_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 
    113 dns_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 
    142 int 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 }