diff options
author | Wojciech Kosior <kwojtus@protonmail.com> | 2020-05-07 01:14:41 +0200 |
---|---|---|
committer | Wojciech Kosior <kwojtus@protonmail.com> | 2020-05-07 01:14:41 +0200 |
commit | fa44d09abdb51031871a0aea76aac1048b9b49aa (patch) | |
tree | 0aae9a01a73bf49ff68f8c7449aa44369bdd4449 | |
parent | 27cbe481d802d65a12e5cc5ed4c5f9faddd9afc6 (diff) | |
download | 0tdns-fa44d09abdb51031871a0aea76aac1048b9b49aa.tar.gz 0tdns-fa44d09abdb51031871a0aea76aac1048b9b49aa.zip |
incorporate response creation and sending from adsuck
-rw-r--r-- | src/receive.c | 284 |
1 files changed, 276 insertions, 8 deletions
diff --git a/src/receive.c b/src/receive.c index eb5fdb2..59697d3 100644 --- a/src/receive.c +++ b/src/receive.c @@ -14,16 +14,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* for strdup() */ +#define _POSIX_C_SOURCE 200809L +#include <string.h> + #include <stdio.h> #include <stdlib.h> #include <err.h> #include <netinet/in.h> - #include <arpa/inet.h> #include <ldns/ldns.h> +#define MAXLINE 256 #define INBUF_SIZE 4096 #define PORT 53 @@ -56,7 +60,7 @@ void printpacket(ldns_pkt *pkt) { char *str = ldns_pkt2str(pkt); - + if (str) { printf("%s", str); LDNS_FREE(str); @@ -64,16 +68,17 @@ printpacket(ldns_pkt *pkt) warnx("could not convert packet to string"); } -void receive_and_print(int so) { +ldns_pkt * +receive_and_print(int so) { uint8_t inbuf[INBUF_SIZE]; ssize_t nb; ldns_status status; ldns_pkt *query_pkt; - + nb = recvfrom(so, inbuf, INBUF_SIZE, 0, &paddr, &plen); if (nb == -1) { if (errno == EINTR || errno == EAGAIN) - return; + return NULL; else err(EXIT_FAILURE, "recvfrom"); } @@ -81,23 +86,286 @@ void receive_and_print(int so) { status = ldns_wire2pkt(&query_pkt, inbuf, (size_t)nb); if (status != LDNS_STATUS_OK) { warnx("bad packet: %s", ldns_get_errorstr_by_id(status)); - return; + return NULL; } else { puts("received packet:"); printpacket(query_pkt); } + return query_pkt; +} + +int +send_response(int so, const char *hostname, ldns_pkt *respkt, uint16_t id) +{ + size_t answer_size; + ldns_status status; + uint8_t *outbuf = NULL; + int rv = 1; + + if (hostname == NULL || respkt == NULL) { + warnx("send_response: invalid parameters"); + return (0); + } + + ldns_pkt_set_id(respkt, id); + status = ldns_pkt2wire(&outbuf, respkt, &answer_size); + if (status != LDNS_STATUS_OK) + warnx("can't create answer: %s", + ldns_get_errorstr_by_id(status)); + else { + puts("response packet:"); + printpacket(respkt); + + if (sendto(so, outbuf, answer_size, 0, &paddr, plen) == -1) + warn("send_response: sendto"); + else { + rv = 0; + + printf("send_response: resolved %s", hostname); + } + } + + if (outbuf) + LDNS_FREE(outbuf); + + return (rv); +} + +char * +hostnamefrompkt(ldns_pkt *pkt, ldns_rr **qrr) +{ + ldns_rr *query_rr; + ldns_buffer *out = NULL; + ldns_rdf *rdf; + char *ret = NULL; + + if (pkt == NULL) + return (NULL); + + query_rr = ldns_rr_list_rr(ldns_pkt_question(pkt), 0); + if (query_rr == NULL) { + warnx("hostnamefrompkt invalid parameters"); + goto done; + } + + out = ldns_buffer_new(LDNS_MAX_DOMAINLEN); + if (out == NULL) { + warnx("no memory for out buffer"); + goto done; + } + + rdf = ldns_rr_owner(query_rr); + if (ldns_rdf2buffer_str_dname(out, rdf) != LDNS_STATUS_OK) { + warnx("can't get hostname"); + goto done; + } + + ret = strdup((char *)ldns_buffer_begin(out)); + if (ret == NULL) { + warn("no memory for hostname"); + goto done; + } + + if (qrr) + *qrr = query_rr; +done: + if (out) + ldns_buffer_free(out); + + return (ret); +} + +int +spoofquery(int so, const char *hostname, ldns_rr *query_rr, u_int16_t id) +{ + ldns_status status; + ldns_rr_list *answer_an = NULL; + ldns_rr_list *answer_ns = NULL; + ldns_rr_list *answer_ad = NULL; + ldns_rr_list *answer_qr = NULL; + ldns_pkt *answer_pkt = NULL; + ldns_rr *myrr = NULL, *myaurr = NULL, *myasrr = NULL; + ldns_rdf *prev = NULL; + char buf[MAXLINE * 2]; + uint8_t *outbuf = NULL; + int rv = 1; + const char *ipaddr = "127.0.0.1"; + + /* answer section */ + answer_an = ldns_rr_list_new(); + if (answer_an == NULL) + goto unwind; + + /* authority section */ + answer_ns = ldns_rr_list_new(); + if (answer_ns == NULL) + goto unwind; + + /* if we have an ip spoof it there */ + if (ipaddr) { + /* an */ + snprintf(buf, sizeof buf, "%s\t%d\tIN\tA\t%s", + hostname, 259200, ipaddr); + status = ldns_rr_new_frm_str(&myrr, buf, 0, NULL, &prev); + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "can't create answer section: %s\n", + ldns_get_errorstr_by_id(status)); + goto unwind; + } + ldns_rr_list_push_rr(answer_an, myrr); + ldns_rdf_deep_free(prev); + prev = NULL; + + /* ns */ + snprintf(buf, sizeof buf, "%s\t%d\tIN\tNS\t%s.", + hostname, 259200, "localhost"); + status = ldns_rr_new_frm_str(&myaurr, buf, 0, NULL, &prev); + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "can't create authority section: %s\n", + ldns_get_errorstr_by_id(status)); + goto unwind; + } + ldns_rr_list_push_rr(answer_ns, myaurr); + ldns_rdf_deep_free(prev); + prev = NULL; + } else { + snprintf(buf, sizeof buf, "%s\t3600\tIN\tSOA\t%s root.%s %s", + hostname, + hostname, + hostname, + "2 3600 900 3600000 3600"); + status = ldns_rr_new_frm_str(&myaurr, buf, 0, NULL, &prev); + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "can't create authority section: %s\n", + ldns_get_errorstr_by_id(status)); + goto unwind; + } + ldns_rr_list_push_rr(answer_ns, myaurr); + ldns_rdf_deep_free(prev); + prev = NULL; + } + + /* question section */ + answer_qr = ldns_rr_list_new(); + if (answer_qr == NULL) + goto unwind; + ldns_rr_list_push_rr(answer_qr, ldns_rr_clone(query_rr)); + + /* additional section */ + answer_ad = ldns_rr_list_new(); + if (answer_ad == NULL) + goto unwind; + if (ipaddr) { + snprintf(buf, sizeof buf, "%s\t%d\tIN\tA\t%s", + "localhost", + 259200, + "127.0.0.1"); + status = ldns_rr_new_frm_str(&myasrr, buf, 0, NULL, &prev); + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "can't create additional section: %s\n", + ldns_get_errorstr_by_id(status)); + goto unwind; + } + ldns_rr_list_push_rr(answer_ad, myasrr); + ldns_rdf_deep_free(prev); + prev = NULL; + + /* V6 */ + snprintf(buf, sizeof buf, "%s\t%d\tIN\tAAAA\t%s", + "localhost", + 259200, + "::1"); + status = ldns_rr_new_frm_str(&myasrr, buf, 0, NULL, &prev); + if (status != LDNS_STATUS_OK) { + fprintf(stderr, "can't create additional section: %s\n", + ldns_get_errorstr_by_id(status)); + goto unwind; + } + ldns_rr_list_push_rr(answer_ad, myasrr); + ldns_rdf_deep_free(prev); + prev = NULL; + } + + /* actual packet */ + answer_pkt = ldns_pkt_new(); + if (answer_pkt == NULL) + goto unwind; + + ldns_pkt_set_qr(answer_pkt, 1); + ldns_pkt_set_aa(answer_pkt, 1); + if (ipaddr == NULL) + ldns_pkt_set_rcode(answer_pkt, LDNS_RCODE_NXDOMAIN); + + ldns_pkt_push_rr_list(answer_pkt, LDNS_SECTION_QUESTION, answer_qr); + ldns_pkt_push_rr_list(answer_pkt, LDNS_SECTION_ANSWER, answer_an); + ldns_pkt_push_rr_list(answer_pkt, LDNS_SECTION_AUTHORITY, answer_ns); + ldns_pkt_push_rr_list(answer_pkt, LDNS_SECTION_ADDITIONAL, answer_ad); + + /* reply to caller */ + if (send_response(so, hostname, answer_pkt, id)) + warnx("send_response failed"); + +unwind: + if (answer_pkt) + ldns_pkt_free(answer_pkt); + if (outbuf) + LDNS_FREE(outbuf); + if (answer_qr) + ldns_rr_list_free(answer_qr); + if (answer_an) + ldns_rr_list_free(answer_an); + if (answer_ns) + ldns_rr_list_free(answer_ns); + if (answer_ad) + ldns_rr_list_free(answer_ad); + + return (rv); +} + +int handle_query(int so) { + ldns_pkt *query_pkt; + ldns_rr *query_rr; + uint16_t id; + int rv = 1; + char *hostname; + + query_pkt = receive_and_print(so); + if (!query_pkt) + return 1; + + hostname = hostnamefrompkt(query_pkt, &query_rr); + if (!hostname) + goto out_cleanup_pkt; + + id = ldns_pkt_id(query_pkt); + + rv = spoofquery(so, hostname, query_rr, id); + + free(hostname); + +out_cleanup_pkt: ldns_pkt_free(query_pkt); + return rv; } int main(int argc, char **argv) { int so; - + if (geteuid()) errx(1, "need root privileges"); so = socket_create(PORT); + /* + * The original adsuck program would additionally do many cool things + * in main(), i.e. parse command line options, drop root privileges, + * setup signal handlers, etc. We could incorporate some of this into + * our program later. I omitted it for now for simplicity and to + * ease porting to windoze if You guys want to do that (but don't + * count on me in this matter). + */ + while (1) - receive_and_print(so); + handle_query(so); } |