summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/addr.c254
-rw-r--r--src/client/addr.h69
-rw-r--r--src/client/binding.c167
-rw-r--r--src/client/binding.h26
-rw-r--r--src/client/client.c177
-rw-r--r--src/client/client.h15
6 files changed, 708 insertions, 0 deletions
diff --git a/src/client/addr.c b/src/client/addr.c
new file mode 100644
index 0000000..82f0e2e
--- /dev/null
+++ b/src/client/addr.c
@@ -0,0 +1,254 @@
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <poll.h>
+
+#include "addr.h"
+
+void create_ip_addr(uint8_t* domain, IpAddr* addr) {
+ addr->type = V4;
+ memcpy(&addr->data.v4, domain, 4);
+}
+
+void create_ip_addr6(uint8_t* domain, IpAddr* addr) {
+ addr->type = V6;
+ memcpy(&addr->data.v6, domain, 16);
+}
+
+void ip_addr_any(IpAddr* addr) {
+ addr->type = V4;
+ addr->data.v4.s_addr = htonl(INADDR_ANY);
+}
+
+void ip_addr_any6(IpAddr* addr) {
+ addr->type = V6;
+ addr->data.v6 = in6addr_any;
+}
+
+bool ip_addr_name(const char* hostname, IpAddr* addr) {
+
+ struct hostent *he;
+ struct in_addr **addr_list;
+ int i;
+
+ if ((he = gethostbyname(hostname)) == NULL) {
+ return false;
+ }
+
+ addr_list = (struct in_addr **) he->h_addr_list;
+
+ for(i = 0; addr_list[i] != NULL; i++) {
+ addr->data.v4 = *addr_list[i];
+ addr->type = V4;
+ return true;
+ }
+
+ return false;
+}
+
+static struct sockaddr_in create_socket_addr_v4(IpAddr addr, uint16_t port) {
+ struct sockaddr_in socketaddr;
+ memset(&socketaddr, 0, sizeof(socketaddr));
+ socketaddr.sin_family = AF_INET;
+ socketaddr.sin_port = htons(port);
+ socketaddr.sin_addr = addr.data.v4;
+ return socketaddr;
+}
+
+static struct sockaddr_in6 create_socket_addr_v6(IpAddr addr, uint16_t port) {
+ struct sockaddr_in6 socketaddr;
+ memset(&socketaddr, 0, sizeof(socketaddr));
+ socketaddr.sin6_family = AF_INET6;
+ socketaddr.sin6_port = htons(port);
+ socketaddr.sin6_addr = addr.data.v6;
+ return socketaddr;
+}
+
+static size_t get_addr_len(AddrType type) {
+ if (type == V4) {
+ return sizeof(struct sockaddr_in);
+ } else if (type == V6) {
+ return sizeof(struct sockaddr_in6);
+ } else {
+ return 0;
+ }
+}
+
+void create_socket_addr(uint16_t port, IpAddr addr, SocketAddr* socket) {
+ socket->type = addr.type;
+ if (addr.type == V4) {
+ socket->data.v4 = create_socket_addr_v4(addr, port);
+ } else if(addr.type == V6) {
+ socket->data.v6 = create_socket_addr_v6(addr, port);
+ } else {
+ exit(EXIT_FAILURE);
+ }
+ socket->len = get_addr_len(addr.type);
+}
+
+#define ADDR_DOMAIN(addr, var) \
+ struct sockaddr* var; \
+ if (addr->type == V4) { \
+ var = (struct sockaddr*) &addr->data.v4; \
+ } else if (addr->type == V6) { \
+ var = (struct sockaddr*) &addr->data.v6; \
+ } else { \
+ return -1; \
+ }
+
+#define ADDR_AFNET(type, var) \
+ int var; \
+ if (type == V4) { \
+ var = AF_INET; \
+ } else if (type == V6) { \
+ var = AF_INET6; \
+ } else { \
+ return -1; \
+ }
+
+int32_t create_udp_socket(AddrType type, UdpSocket* sock) {
+ ADDR_AFNET(type, __domain)
+ sock->type = type;
+ sock->sockfd = socket(__domain, SOCK_DGRAM, 0);
+ return sock->sockfd;
+}
+
+int32_t bind_udp_socket(SocketAddr* addr, UdpSocket* sock) {
+ if (addr->type == V6) {
+ int v6OnlyEnabled = 0;
+ int32_t res = setsockopt(
+ sock->sockfd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ &v6OnlyEnabled,
+ sizeof(v6OnlyEnabled)
+ );
+ if (res < 0) return res;
+ }
+ ADDR_DOMAIN(addr, __addr)
+ return bind(sock->sockfd, __addr, addr->len);
+}
+
+static bool timeout (int connfd, int ms) {
+ struct pollfd fd;
+ int res;
+
+ fd.fd = connfd;
+ fd.events = POLLIN;
+ res = poll(&fd, 1, ms);
+ return res == 0;
+}
+
+int32_t read_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr) {
+ clientaddr->type = socket->type;
+ clientaddr->len = get_addr_len(socket->type);
+ ADDR_DOMAIN(clientaddr, __addr)
+ if (timeout(socket->sockfd, 1000)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return recvfrom(
+ socket->sockfd,
+ buffer,
+ (size_t) len,
+ MSG_WAITALL,
+ __addr,
+ (uint32_t*) &clientaddr->len
+ );
+}
+
+int32_t write_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr) {
+ ADDR_DOMAIN(clientaddr, __addr)
+ return sendto(
+ socket->sockfd,
+ buffer,
+ (size_t) len,
+ MSG_CONFIRM,
+ __addr,
+ (uint32_t) clientaddr->len
+ );
+}
+
+int32_t close_udp_socket(UdpSocket* socket) {
+ return close(socket->sockfd);
+}
+
+int32_t create_tcp_socket(AddrType type, TcpSocket* sock) {
+ ADDR_AFNET(type, __domain)
+ sock->type = type;
+ sock->sockfd = socket(__domain, SOCK_STREAM, 0);
+ return sock->sockfd;
+}
+
+int32_t bind_tcp_socket(SocketAddr* addr, TcpSocket* sock) {
+ if (addr->type == V6) {
+ int v6OnlyEnabled = 0;
+ int32_t res = setsockopt(
+ sock->sockfd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ &v6OnlyEnabled,
+ sizeof(v6OnlyEnabled)
+ );
+ if (res < 0) return res;
+ }
+ ADDR_DOMAIN(addr, __addr)
+ return bind(sock->sockfd, __addr, addr->len);
+}
+
+int32_t listen_tcp_socket(TcpSocket* socket, uint32_t max) {
+ return listen(socket->sockfd, max);
+}
+
+int32_t accept_tcp_socket(TcpSocket* socket, TcpStream* stream) {
+ stream->clientaddr.type = socket->type;
+ memset(&stream->clientaddr, 0, sizeof(SocketAddr));
+ SocketAddr* addr = &stream->clientaddr;
+ ADDR_DOMAIN(addr, __addr)
+ stream->streamfd = accept(
+ socket->sockfd,
+ __addr,
+ (uint32_t*) &stream->clientaddr.len
+ );
+ return stream->streamfd;
+}
+
+int32_t close_tcp_socket(TcpSocket* socket) {
+ return close(socket->sockfd);
+}
+
+int32_t connect_tcp_stream(SocketAddr* servaddr, TcpStream* stream) {
+ TcpSocket socket;
+ int32_t res = create_tcp_socket(servaddr->type, &socket);
+ if (res < 0) return res;
+ stream->clientaddr = *servaddr;
+ stream->streamfd = socket.sockfd;
+ ADDR_DOMAIN(servaddr, __addr)
+ return connect(
+ socket.sockfd,
+ __addr,
+ servaddr->len
+ );
+}
+
+int32_t read_tcp_stream(TcpStream* stream, void* buffer, uint16_t len) {
+ if (timeout(stream->streamfd, 3000)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return recv(stream->streamfd, buffer, len, MSG_WAITALL);
+}
+
+int32_t write_tcp_stream(TcpStream* stream, void* buffer, uint16_t len) {
+ return send(stream->streamfd, buffer, len, MSG_NOSIGNAL);
+}
+
+int32_t close_tcp_stream(TcpStream* stream) {
+ return close(stream->streamfd);
+}
diff --git a/src/client/addr.h b/src/client/addr.h
new file mode 100644
index 0000000..28c6066
--- /dev/null
+++ b/src/client/addr.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "../packet/record.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+typedef enum {
+ V4,
+ V6
+} AddrType;
+
+typedef struct {
+ AddrType type;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } data;
+} IpAddr;
+
+void create_ip_addr(uint8_t* domain, IpAddr* addr);
+void create_ip_addr6(uint8_t* domain, IpAddr* addr);
+void ip_addr_any(IpAddr* addr);
+void ip_addr_any6(IpAddr* addr);
+bool ip_addr_name(const char* name, IpAddr* addr);
+
+typedef struct {
+ AddrType type;
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } data;
+ size_t len;
+} SocketAddr;
+
+void create_socket_addr(uint16_t port, IpAddr addr, SocketAddr* socket);
+
+typedef struct {
+ AddrType type;
+ uint32_t sockfd;
+} UdpSocket;
+
+int32_t create_udp_socket(AddrType type, UdpSocket* socket);
+int32_t bind_udp_socket(SocketAddr* addr, UdpSocket* socket);
+int32_t read_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr);
+int32_t write_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr);
+int32_t close_udp_socket(UdpSocket* socket);
+
+typedef struct {
+ AddrType type;
+ uint32_t sockfd;
+} TcpSocket;
+
+typedef struct {
+ SocketAddr clientaddr;
+ uint32_t streamfd;
+} TcpStream;
+
+int32_t create_tcp_socket(AddrType type, TcpSocket* socket);
+int32_t bind_tcp_socket(SocketAddr* addr, TcpSocket* socket);
+int32_t listen_tcp_socket(TcpSocket* socket, uint32_t max);
+int32_t accept_tcp_socket(TcpSocket* socket, TcpStream* stream);
+int32_t close_tcp_socket(TcpSocket* socket);
+int32_t connect_tcp_stream(SocketAddr* servaddr, TcpStream* stream);
+int32_t read_tcp_stream(TcpStream* stream, void* buffer, uint16_t len);
+int32_t write_tcp_stream(TcpStream* stream, void* buffer, uint16_t len);
+int32_t close_tcp_stream(TcpStream* stream);
diff --git a/src/client/binding.c b/src/client/binding.c
new file mode 100644
index 0000000..9917a50
--- /dev/null
+++ b/src/client/binding.c
@@ -0,0 +1,167 @@
+#include <ctype.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "addr.h"
+#include "binding.h"
+
+static void read_to_packet(uint8_t* buf, uint16_t len, Packet* packet) {
+ PacketBuffer* pkbuffer = buffer_create(len);
+ for (int i = 0; i < len; i++) {
+ buffer_write(pkbuffer, buf[i]);
+ }
+ buffer_seek(pkbuffer, 0);
+ read_packet(pkbuffer, packet);
+ buffer_free(pkbuffer);
+}
+
+static bool read_udp(Connection* connection, Packet* packet) {
+ uint8_t buffer[512];
+ int32_t n = read_udp_socket(
+ &connection->sock.udp.udp,
+ buffer,
+ 512,
+ &connection->sock.udp.clientaddr
+ );
+ if (n < 0) {
+ return false;
+ }
+ read_to_packet(buffer, n, packet);
+ return true;
+}
+
+static bool read_tcp(Connection* connection, Packet* packet) {
+ uint16_t len;
+ if ( read_tcp_stream(
+ &connection->sock.tcp,
+ &len,
+ sizeof(uint16_t)
+ ) < 2) {
+ return false;
+ }
+ len = ntohs(len);
+
+ uint8_t buffer[len];
+ if (read_tcp_stream(
+ &connection->sock.tcp,
+ buffer,
+ len
+ ) < len) {
+ return false;
+ }
+
+ read_to_packet(buffer, len, packet);
+ return true;
+}
+
+static bool read_connection(Connection* connection, Packet* packet) {
+ if (connection->type == UDP) {
+ return read_udp(connection, packet);
+ } else if (connection->type == TCP) {
+ return read_tcp(connection, packet);
+ }
+ return false;
+}
+
+static bool write_udp(Connection* connection, uint8_t* buf, uint16_t len) {
+ if (len > 512) {
+ buf[2] = buf[2] | 0x03;
+ len = 512;
+ }
+ return write_udp_socket(
+ &connection->sock.udp.udp,
+ buf,
+ len,
+ &connection->sock.udp.clientaddr
+ ) == len;
+}
+
+static bool write_tcp(Connection* connection, uint8_t* buf, uint16_t len) {
+ uint16_t net_len = htons(len);
+ if (write_tcp_stream(
+ &connection->sock.tcp,
+ &net_len,
+ sizeof(uint16_t)
+ ) < 0) {
+ return false;
+ }
+
+ if (write_tcp_stream(
+ &connection->sock.tcp,
+ buf,
+ len
+ ) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool write_connection(Connection* connection, Packet* packet) {
+ PacketBuffer* pkbuffer = buffer_create(64);
+ write_packet(pkbuffer, packet);
+ uint16_t len = buffer_get_size(pkbuffer);
+ uint8_t* buffer = buffer_get_ptr(pkbuffer);
+ bool success = false;
+ if(connection->type == UDP) {
+ success = write_udp(connection, buffer, len);
+ } else if(connection->type == TCP) {
+ success = write_tcp(connection, buffer, len);
+ };
+ buffer_free(pkbuffer);
+ return success;
+}
+
+void free_connection(Connection* connection) {
+ if (connection->type == TCP) {
+ close_tcp_stream(&connection->sock.tcp);
+ }
+}
+
+static bool create_udp_request(SocketAddr* addr, Connection* request) {
+ if ( create_udp_socket(addr->type, &request->sock.udp.udp) < 0) {
+ return false;
+ }
+ request->sock.udp.clientaddr = *addr;
+ return true;
+}
+
+static bool create_tcp_request(SocketAddr* addr, Connection* request) {
+ if( connect_tcp_stream(addr, &request->sock.tcp) < 0) {
+ return false;
+ }
+ return true;
+}
+
+bool create_request(BindingType type, SocketAddr* addr, Connection* request) {
+ request->type = type;
+ if (type == UDP) {
+ return create_udp_request(addr, request);
+ } else if (type == TCP) {
+ return create_tcp_request(addr, request);
+ } else {
+ return true;
+ }
+}
+
+bool request_packet(Connection* request, Packet* in, Packet* out) {
+ if (!write_connection(request, in)) {
+ return false;
+ }
+ if (!read_connection(request, out)) {
+ return false;
+ }
+ return true;
+}
+
+void free_request(Connection* connection) {
+ if (connection->type == UDP) {
+ close_udp_socket(&connection->sock.udp.udp);
+ } else if (connection->type == TCP) {
+ close_tcp_stream(&connection->sock.tcp);
+ }
+}
diff --git a/src/client/binding.h b/src/client/binding.h
new file mode 100644
index 0000000..ea02c98
--- /dev/null
+++ b/src/client/binding.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "../packet/packet.h"
+#include "addr.h"
+
+#include <netinet/in.h>
+
+typedef enum {
+ UDP,
+ TCP
+} BindingType;
+
+typedef struct {
+ BindingType type;
+ union {
+ struct {
+ UdpSocket udp;
+ SocketAddr clientaddr;
+ } udp;
+ TcpStream tcp;
+ } sock;
+} Connection;
+
+bool create_request(BindingType type, SocketAddr* addr, Connection* request);
+bool request_packet(Connection* request, Packet* in, Packet* out);
+void free_request(Connection* connection);
diff --git a/src/client/client.c b/src/client/client.c
new file mode 100644
index 0000000..31ef589
--- /dev/null
+++ b/src/client/client.c
@@ -0,0 +1,177 @@
+#include "client.h"
+#include "../packet/question.h"
+#include "addr.h"
+#include "binding.h"
+
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+
+void resolve_default_server(IpAddr* addr) {
+ res_init();
+ addr->data.v4 = _res.nsaddr_list[0].sin_addr;
+ addr->type = V4;
+}
+
+static void push_question(Question** buf, Question q, uint8_t* amount, uint8_t* capacity) {
+ if (*amount >= *capacity) {
+ *capacity *= 2;
+ *buf = realloc(*buf, sizeof(Question) * *capacity);
+ }
+ (*buf)[*amount] = q;
+ (*amount)++;
+}
+
+void resolve_questions(int argc, char** argv, uint8_t* len, Question** questions) {
+ assert(argc > 0);
+
+ uint8_t amount = 0;
+ uint8_t capacity = 1;
+ Question* buf = malloc(sizeof(Question) * capacity);
+
+ Question q;
+ RecordType t;
+ if (argc == 1 && str_to_qtype(argv[0], &t)) {
+ create_question("", NS, &q);
+ push_question(&buf, q, &amount, &capacity);
+ *questions = buf;
+ *len = 1;
+ return;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ if (str_to_qtype(argv[i], &t)) {
+ if (i + 1 == argc) break;
+ create_question(argv[i+1], t, &q);
+ i++;
+ } else if (i + 1 < argc && str_to_qtype(argv[i+1], &t)){
+ create_question(argv[i], t, &q);
+ i++;
+ } else {
+ create_question(argv[i], A, &q);
+ }
+ push_question(&buf, q, &amount, &capacity);
+ }
+
+ *questions = buf;
+ *len = amount;
+}
+
+static void print_result(Packet* packet, const char* type, uint32_t ms) {
+ printf(">> Recieved response\n");
+ printf("Id: %hu, Code: %s\n",
+ packet->header.id,
+ str_from_code(packet->header.rescode)
+ );
+ printf("Questions: %hu, Answers: %hu, Authorities: %hu, Resources: %hu\n",
+ packet->header.questions,
+ packet->header.answers,
+ packet->header.authoritative_entries,
+ packet->header.resource_entries
+ );
+
+ if (packet->header.questions > 0) {
+ printf("\n>> Question Section\n");
+ for (uint16_t i = 0; i < packet->header.questions; i++) {
+ print_question(&packet->questions[i]);
+ }
+ }
+
+ if (packet->header.answers > 0) {
+ printf("\n>> Answer Section\n");
+ for (uint16_t i = 0; i < packet->header.answers; i++) {
+ print_record(&packet->answers[i]);
+ }
+ }
+
+ if (packet->header.authoritative_entries > 0) {
+ printf("\n>> Authority Section\n");
+ for (uint16_t i = 0; i < packet->header.authoritative_entries; i++) {
+ print_record(&packet->authorities[i]);
+ }
+ }
+
+ if (packet->header.resource_entries > 0) {
+ printf("\n>> Resource Section\n");
+ for (uint16_t i = 0; i < packet->header.resource_entries; i++) {
+ print_record(&packet->resources[i]);
+ }
+ }
+
+ printf("\n>> Query time: %ums (%s)\n", ms, type);
+}
+
+void resolve(IpAddr server, Options options, Question* questions, uint8_t len) {
+ struct timeval stop, start;
+ gettimeofday(&start, NULL);
+
+ SocketAddr addr;
+ create_socket_addr(options.port, server, &addr);
+
+ Packet req;
+ memset(&req, 0, sizeof(Packet));
+ srand(time(NULL));
+ req.header.id = rand() % 65536;
+ req.header.questions = len;
+ req.header.recursion_desired = true;
+ req.questions = questions;
+
+ Connection conn;
+ if (options.force_tcp) goto tcp;
+
+ if (!create_request(UDP, &addr, &conn)) {
+ printf("error: failed to create udp request: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ Packet res;
+ if (!request_packet(&conn, &req, &res)) {
+ free_request(&conn);
+ free_packet(&req);
+ printf("error: failed to request udp packet: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ free_request(&conn);
+
+ if (!res.header.truncated_message) {
+ gettimeofday(&stop, NULL);
+ uint32_t ms = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
+
+ print_result(&res, "UDP", ms / 1000);
+ free_packet(&req);
+ free_packet(&res);
+ return;
+ }
+
+ free_packet(&res);
+ printf("Response truncated, retrying in TCP...\n");
+
+tcp:
+
+ if (!create_request(TCP, &addr, &conn)) {
+ printf("error: failed to create tcp request: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (!request_packet(&conn, &req, &res)) {
+ free_request(&conn);
+ printf("error: failed to request tcp packet: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ gettimeofday(&stop, NULL);
+ uint32_t ms = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
+
+ print_result(&res, "TCP", ms / 1000);
+
+ free_request(&conn);
+ free_packet(&req);
+ free_packet(&res);
+}
+
diff --git a/src/client/client.h b/src/client/client.h
new file mode 100644
index 0000000..7e0e391
--- /dev/null
+++ b/src/client/client.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "addr.h"
+#include "../packet/packet.h"
+
+typedef struct {
+ uint16_t port;
+ bool force_tcp;
+} Options;
+
+void resolve_default_server(IpAddr* addr);
+
+void resolve_questions(int argc, char** argv, uint8_t* len, Question** questions);
+
+void resolve(IpAddr server, Options options, Question* questions, uint8_t lem);