From ee7d1e086b07b004320ea7b1b25315574c2513f4 Mon Sep 17 00:00:00 2001
From: Lorenz Stechauner <lorenz.stechauner@necronda.net>
Date: Mon, 28 Dec 2020 23:32:37 +0100
Subject: [PATCH] Get hostnames with reverse DNS

---
 src/client.c          | 46 +++++++++++++++++++++++++++++++++++--------
 src/fastcgi.c         | 25 ++++++++---------------
 src/necronda-server.c |  9 ++++++++-
 src/necronda-server.h |  2 +-
 4 files changed, 55 insertions(+), 27 deletions(-)

diff --git a/src/client.c b/src/client.c
index baf3893..dcaf2cf 100644
--- a/src/client.c
+++ b/src/client.c
@@ -295,10 +295,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
         if (ret != 0) {
             if (ret < 0) {
                 goto abort;
-            } else {
-                sprintf(err_msg, "Unable to communicate with PHP-FPM.");
             }
-            res.status = http_get_status(502);
             goto respond;
         }
         char *status = http_get_header_field(&res.hdr, "Status");
@@ -427,15 +424,39 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
 int client_connection_handler(sock *client, unsigned long client_num) {
     struct timespec begin, end;
     int ret, req_num;
-    char buf[16];
+    char buf[1024];
 
     clock_gettime(CLOCK_MONOTONIC, &begin);
 
-    // TODO get geoip data for ip address
-    // TODO Reverse DNS request
-    client_host_str = client_addr_str;
+    if (dns_server != NULL) {
+        sprintf(buf, "dig @%s +short +time=1 -x %s", dns_server, client_addr_str);
+        FILE *dig = popen(buf, "r");
+        if (dig == NULL) {
+            fprintf(stderr, ERR_STR "Unable to start dig: %s" CLR_STR "\n", strerror(errno));
+            goto dig_err;
+        }
+        unsigned long read = fread(buf, 1, sizeof(buf), dig);
+        ret = pclose(dig);
+        if (ret != 0) {
+            fprintf(stderr, ERR_STR "Dig terminated with exit code %i" CLR_STR "\n", ret);
+            goto dig_err;
+        }
+        char *ptr = memchr(buf, '\n', read);
+        if (ptr == buf || ptr == NULL) {
+            goto dig_err;
+        }
+        ptr[-1] = 0;
+        client_host_str = malloc(strlen(buf) + 1);
+        strcpy(client_host_str, buf);
+    } else {
+        dig_err:
+        client_host_str = NULL;
+    }
 
-    print("Connection accepted from %s (%s) [%s]", client_addr_str, client_host_str, "N/A");
+    // TODO get geoip data for ip address
+
+    print("Connection accepted from %s %s%s%s[%s]", client_addr_str, client_host_str != NULL ? "(" : "",
+          client_host_str != NULL ? client_host_str : "", client_host_str != NULL ? ") " : "", "N/A");
 
     client_timeout.tv_sec = CLIENT_TIMEOUT;
     client_timeout.tv_usec = 0;
@@ -524,9 +545,18 @@ int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *
 
     ret = client_connection_handler(client, client_num);
     free(client_addr_str_ptr);
+    client_addr_str_ptr = NULL;
     free(server_addr_str_ptr);
+    server_addr_str_ptr = NULL;
+    if (client_host_str != NULL) {
+        free(client_host_str);
+        client_host_str = NULL;
+    }
     free(log_conn_prefix);
+    log_conn_prefix = NULL;
     free(log_req_prefix);
+    log_req_prefix = NULL;
     free(log_client_prefix);
+    log_client_prefix = NULL;
     return ret;
 }
diff --git a/src/fastcgi.c b/src/fastcgi.c
index 1269f7e..b0790bf 100644
--- a/src/fastcgi.c
+++ b/src/fastcgi.c
@@ -118,17 +118,8 @@ int fastcgi_init(fastcgi_conn *conn, unsigned int client_num, unsigned int req_n
     addr = (struct sockaddr_in6 *) &addr_storage;
     sprintf(buf0, "%i", addr->sin6_port);
     param_ptr = fastcgi_add_param(param_ptr, "REMOTE_PORT", buf0);
-
-    char addr_str[INET6_ADDRSTRLEN];
-    char *addr_ptr;
-    inet_ntop(addr->sin6_family, (void *) &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN);
-    if (strncmp(addr_str, "::ffff:", 7) == 0) {
-        addr_ptr = addr_str + 7;
-    } else {
-        addr_ptr = addr_str;
-    }
-    param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", addr_ptr);
-    param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", addr_ptr);
+    param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", client_addr_str);
+    param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", client_host_str != NULL ? client_host_str : client_addr_str);
     //param_ptr = fastcgi_add_param(param_ptr, "REMOTE_IDENT", "");
     //param_ptr = fastcgi_add_param(param_ptr, "REMOTE_USER", "");
 
@@ -287,12 +278,12 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
             res->status = http_get_status(502);
             sprintf(err_msg, "Unable to communicate with PHP-FPM.");
             print(ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR, strerror(errno));
-            return -1;
+            return 1;
         } else if (ret != sizeof(header)) {
             res->status = http_get_status(502);
             sprintf(err_msg, "Unable to communicate with PHP-FPM.");
             print(ERR_STR "Unable to receive from PHP-FPM" CLR_STR);
-            return -1;
+            return 1;
         }
         req_id = (header.requestIdB1 << 8) | header.requestIdB0;
         content_len = (header.contentLengthB1 << 8) | header.contentLengthB0;
@@ -303,13 +294,13 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
             sprintf(err_msg, "Unable to communicate with PHP-FPM.");
             print(ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR, strerror(errno));
             free(content);
-            return -1;
+            return 1;
         } else if (ret != (content_len + header.paddingLength)) {
             res->status = http_get_status(502);
             sprintf(err_msg, "Unable to communicate with PHP-FPM.");
             print(ERR_STR "Unable to receive from PHP-FPM" CLR_STR);
             free(content);
-            return -1;
+            return 1;
         }
 
         if (req_id != conn->req_id) {
@@ -329,7 +320,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
             close(conn->socket);
             conn->socket = 0;
             free(content);
-            return -2;
+            return 1;
         } else if (header.type == FCGI_STDERR) {
             err = err || fastcgi_php_error(content, content_len, err_msg);
         } else if (header.type == FCGI_STDOUT) {
@@ -342,7 +333,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
     }
     if (err) {
         res->status = http_get_status(500);
-        return -3;
+        return 2;
     }
 
     conn->out_buf = content;
diff --git a/src/necronda-server.c b/src/necronda-server.c
index b1c3f84..43dcd82 100644
--- a/src/necronda-server.c
+++ b/src/necronda-server.c
@@ -189,10 +189,11 @@ int main(int argc, const char *argv[]) {
         const char *arg = argv[i];
         unsigned long len = strlen(arg);
         if ((len == 2 && strncmp(arg, "-h", 2) == 0) || (len == 6 && strncmp(arg, "--help", 6) == 0)) {
-            printf("Usage: necronda-server [-h] -w <PATH> -c <CERT-FILE> -p <KEY-FILE> [-g <DB-FILE>]\n"
+            printf("Usage: necronda-server [-h] -w <PATH> -c <CERT-FILE> -p <KEY-FILE> [-g <DB-FILE>] [-d <DNS-SERVER>]\n"
                    "\n"
                    "Options:\n"
                    "  -c, --cert <CERT-FILE>    path to the full chain certificate file\n"
+                   "  -d, --dns <DNS-SERVER>    ip address or hostname of a DNS server for dig\n"
                    "  -g, --geoip <DB-FILE>     path to a Maxmind GeoIP Database file\n"
                    "  -h, --help                print this dialogue\n"
                    "  -p, --privkey <KEY-FILE>  path to the private key file\n"
@@ -222,6 +223,12 @@ int main(int argc, const char *argv[]) {
                 return 1;
             }
             geoip_file = argv[++i];
+        } else if ((len == 2 && strncmp(arg, "-d", 2) == 0) || (len == 5 && strncmp(arg, "--dns", 5) == 0)) {
+            if (i == argc - 1) {
+                fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --dns <DNS-SERVER>" CLR_STR "\n", arg);
+                return 1;
+            }
+            dns_server = argv[++i];
         } else {
             fprintf(stderr, ERR_STR "Unable to parse argument '%s'" CLR_STR "\n", arg);
             return 1;
diff --git a/src/necronda-server.h b/src/necronda-server.h
index 77f21ce..3c71da3 100644
--- a/src/necronda-server.h
+++ b/src/necronda-server.h
@@ -66,7 +66,7 @@
 int sockets[NUM_SOCKETS];
 pid_t children[MAX_CHILDREN];
 
-const char *cert_file, *key_file, *webroot_base, *geoip_file;
+const char *cert_file, *key_file, *webroot_base, *geoip_file, *dns_server;
 
 typedef struct {
     unsigned int enc:1;