commit c82c80e62bfab015e1ad05c6dd6b981962fca604
Author: typable <contact@typable.dev>
Date: Sat, 14 Dec 2024 22:34:07 +0100
feat: Initial commit
Diffstat:
4 files changed, 219 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,13 @@
+CC=cc
+CFLAGS=-Wall -Wextra -Werror -pedantic
+LIBS=-lcurl
+
+build: src/*.c
+ $(CC) -o dns $(CFLAGS) $(LIBS) $^
+
+install: build
+ cp dns /usr/local/bin/
+ rm ./dns
+
+clean:
+ rm ./dns
diff --git a/README.md b/README.md
@@ -0,0 +1 @@
+# dns
diff --git a/dns.sh b/dns.sh
@@ -0,0 +1,5 @@
+export DNS_UPDATE_URL="https://dynamicdns.park-your-domain.com/update?host={HOST}&domain={DOMAIN}&password={PASSWD}&ip={IP_ADDR}"
+export DNS_LOOKUP_URL="https://dns.google.com/resolve?name={HOST}.{DOMAIN}&type=A"
+export DNS_RESOLVE_URL="http://checkip.amazonaws.com"
+
+./dns "<HOST>" "<DOMAIN>" "<PASSWD>"
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,200 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+#define LIB_STR_IMPL
+#include <libstr.h>
+
+#define DNS_ENV_UPDATE "DNS_UPDATE_URL"
+#define DNS_ENV_LOOKUP "DNS_LOOKUP_URL"
+#define DNS_ENV_RESOLVE "DNS_RESOLVE_URL"
+
+#define DNS_SYMBOL_HOST "HOST"
+#define DNS_SYMBOL_DOMAIN "DOMAIN"
+#define DNS_SYMBOL_IP_ADDR "IP_ADDR"
+#define DNS_SYMBOL_PASSWD "PASSWD"
+
+#define DNS_DOMAIN_WILDCARD "@"
+
+typedef enum {
+ DNS_ERR_OK,
+ DNS_ERR_FAIL,
+} dns_error_t;
+
+typedef struct {
+ char *host;
+ char *domain;
+ char *passwd;
+} dns_domain_t;
+
+size_t dns_curl_write(void *ptr, size_t size, size_t nmemb, void *data) {
+ size_t len = size * nmemb;
+ char **curl_data = (char **) data;
+ str_append_len(curl_data, ptr, len);
+ return len;
+}
+
+dns_error_t dns_update(char *url, dns_domain_t *domain, char *ip_addr) {
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ fprintf(stderr, "CURL init failed!\n");
+ return DNS_ERR_FAIL;
+ }
+ char *resolved_url = url;
+ resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_HOST "}", domain->host);
+ resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_DOMAIN "}", domain->domain);
+ resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_PASSWD "}", domain->passwd);
+ resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_IP_ADDR "}", ip_addr);
+ char *curl_data = NULL;
+ curl_easy_setopt(curl, CURLOPT_URL, resolved_url);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dns_curl_write);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
+ CURLcode code = curl_easy_perform(curl);
+ free(resolved_url);
+ if (code != CURLE_OK) {
+ curl_easy_cleanup(curl);
+ fprintf(stderr, "CURL fetch failed!\n%s\n", curl_easy_strerror(code));
+ return DNS_ERR_FAIL;
+ }
+ curl_easy_cleanup(curl);
+ for (size_t i = 0; i < strlen(curl_data); i++) {
+ if (strncmp(curl_data + i, "<ErrCount>0</ErrCount>", 22) == 0) {
+ free(curl_data);
+ return DNS_ERR_OK;
+ }
+ }
+ fprintf(stderr, "DNS update error:\n%s\n", curl_data);
+ if (curl_data != NULL) {
+ free(curl_data);
+ }
+ return DNS_ERR_FAIL;
+}
+
+dns_error_t dns_lookup(char *url, dns_domain_t *domain, char **ip_addr) {
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ fprintf(stderr, "CURL init failed!\n");
+ return DNS_ERR_FAIL;
+ }
+ char *resolved_url = url;
+ resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_HOST "}", domain->host);
+ resolved_url = str_replace_all(resolved_url, "{" DNS_SYMBOL_DOMAIN "}", domain->domain);
+ char *curl_data = NULL;
+ curl_easy_setopt(curl, CURLOPT_URL, resolved_url);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dns_curl_write);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
+ CURLcode code = curl_easy_perform(curl);
+ free(resolved_url);
+ if (code != CURLE_OK) {
+ curl_easy_cleanup(curl);
+ fprintf(stderr, "CURL fetch failed!\n%s\n", curl_easy_strerror(code));
+ return DNS_ERR_FAIL;
+ }
+ curl_easy_cleanup(curl);
+ size_t set = 0;
+ for (size_t i = 0; i < strlen(curl_data);) {
+ if (strncmp(curl_data + i, "\"data\":\"", 8) == 0) {
+ i += 8;
+ set = i;
+ continue;
+ }
+ if (set > 0 && curl_data[i] == '"') {
+ str_append_len(ip_addr, curl_data + set, i - set);
+ break;
+ }
+ i++;
+ }
+ if (curl_data != NULL) {
+ free(curl_data);
+ }
+ return DNS_ERR_OK;
+}
+
+dns_error_t dns_resolve(char *url, char **ip_addr) {
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ fprintf(stderr, "CURL init failed!\n");
+ return DNS_ERR_FAIL;
+ }
+ char *curl_data = NULL;
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dns_curl_write);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);
+ CURLcode code = curl_easy_perform(curl);
+ if (code != CURLE_OK) {
+ curl_easy_cleanup(curl);
+ fprintf(stderr, "CURL fetch failed!\n%s\n", curl_easy_strerror(code));
+ return DNS_ERR_FAIL;
+ }
+ curl_easy_cleanup(curl);
+ for (size_t i = 0; i < strlen(curl_data); i++) {
+ if (curl_data[i] == '\n') {
+ str_append_len(ip_addr, curl_data, i);
+ break;
+ }
+ }
+ if (curl_data != NULL) {
+ free(curl_data);
+ }
+ return DNS_ERR_OK;
+}
+
+int main(int argc, char **argv) {
+ if (argc != 4) {
+ fprintf(stderr, "Invalid arguments!\nUsage: dns <host> <domain> <passwd>\n");
+ return EXIT_FAILURE;
+ }
+ dns_domain_t domain = {
+ .host = argv[1],
+ .domain = argv[2],
+ .passwd = argv[3],
+ };
+ if (strcmp(domain.host, DNS_DOMAIN_WILDCARD) == 0) {
+ fprintf(stdout, "DNS domain: %s\n", domain.domain);
+ }
+ else {
+ fprintf(stdout, "DNS domain: %s.%s\n", domain.host, domain.domain);
+ }
+ char *dns_update_url = getenv(DNS_ENV_UPDATE);
+ if (dns_update_url == NULL) {
+ fprintf(stderr, "Environment variable missing!\nDefine: " DNS_ENV_UPDATE "\n");
+ return EXIT_FAILURE;
+ }
+ char *dns_lookup_url = getenv(DNS_ENV_LOOKUP);
+ if (dns_lookup_url == NULL) {
+ fprintf(stderr, "Environment variable missing!\nDefine: " DNS_ENV_LOOKUP "\n");
+ return EXIT_FAILURE;
+ }
+ char *dns_resolve_url = getenv(DNS_ENV_RESOLVE);
+ if (dns_resolve_url == NULL) {
+ fprintf(stderr, "Environment variable missing!\nDefine: " DNS_ENV_RESOLVE "\n");
+ return EXIT_FAILURE;
+ }
+ char *ip_addr = NULL;
+ if (dns_resolve(dns_resolve_url, &ip_addr) != DNS_ERR_OK) {
+ fprintf(stderr, "IP address resolution failed!\n");
+ return EXIT_FAILURE;
+ }
+ fprintf(stdout, "DNS resolve IP_ADDR: %s\n", ip_addr);
+ char *domain_ip_addr = NULL;
+ if (dns_lookup(dns_lookup_url, &domain, &domain_ip_addr) != DNS_ERR_OK) {
+ fprintf(stderr, "DNS lookup failed!\n");
+ return EXIT_FAILURE;
+ }
+ fprintf(stdout, "DNS lookup DOMAIN_IP_ADDR: %s\n", domain_ip_addr);
+ if (strcmp(ip_addr, domain_ip_addr) != 0) {
+ if (dns_update(dns_update_url, &domain, ip_addr) != DNS_ERR_OK) {
+ free(ip_addr);
+ free(domain_ip_addr);
+ fprintf(stderr, "DNS update FAILED!\n");
+ return EXIT_FAILURE;
+ }
+ fprintf(stdout, "DNS update SUCCESSFUL.\n");
+ }
+ else {
+ fprintf(stdout, "DNS domain already UP-TO-DATE.\n");
+ }
+ free(ip_addr);
+ free(domain_ip_addr);
+ return EXIT_SUCCESS;
+}