test_requestor.c 5.09 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*  Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

17
#include <assert.h>
18
#include <tap/basic.h>
19
#include <pthread.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <fcntl.h>
23

24 25
#include "libknot/descriptor.h"
#include "libknot/errcode.h"
26 27
#include "knot/query/layer.h"
#include "knot/query/requestor.h"
28
#include "contrib/mempattern.h"
29 30
#include "contrib/net.h"
#include "contrib/sockaddr.h"
31
#include "contrib/ucw/mempool.h"
32 33 34

/* @note Purpose of this test is not to verify process_answer functionality,
 *       but simply if the requesting/receiving works, so mirror is okay. */
35
static int reset(knot_layer_t *ctx) { return KNOT_STATE_PRODUCE; }
36
static int begin(knot_layer_t *ctx, void *module_param) { return reset(ctx); }
37
static int finish(knot_layer_t *ctx) { return reset(ctx); }
38 39
static int in(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_DONE; }
static int out(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_CONSUME; }
40

41
static const int TIMEOUT = 2000;
42

43
/*! \brief Dummy answer processing module. */
44
const knot_layer_api_t dummy_module = {
45
        &begin, &reset, &finish, &in, &out
46 47
};

48 49 50 51 52 53 54
static void set_blocking_mode(int sock)
{
	int flags = fcntl(sock, F_GETFL);
	flags &= ~O_NONBLOCK;
	fcntl(sock, F_SETFL, flags);
}

55
static void *responder_thread(void *arg)
56
{
57 58
	int fd = *(int *)arg;

59
	set_blocking_mode(fd);
60
	uint8_t buf[KNOT_WIRE_MAX_PKTSIZE] = { 0 };
61
	while (true) {
62
		int client = accept(fd, NULL, NULL);
63 64 65
		if (client < 0) {
			break;
		}
66
		int len = net_dns_tcp_recv(client, buf, sizeof(buf), -1);
67 68 69 70 71
		if (len < KNOT_WIRE_HEADER_SIZE) {
			close(client);
			break;
		}
		knot_wire_set_qr(buf);
72
		net_dns_tcp_send(client, buf, len, -1);
73 74
		close(client);
	}
75

76 77 78 79 80
	return NULL;
}

/* Test implementations. */

81 82 83
static struct knot_request *make_query(struct knot_requestor *requestor,
                                       const struct sockaddr_storage *dst,
                                       const struct sockaddr_storage *src)
84
{
85
	knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm);
86
	assert(pkt);
87 88
	static const knot_dname_t *root = (uint8_t *)"";
	knot_pkt_put_question(pkt, root, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
89

90
	return knot_request_make(requestor->mm, (struct sockaddr *)dst,
Jan Včelák's avatar
Jan Včelák committed
91
	                         (struct sockaddr *)src, pkt, NULL, 0);
92 93
}

94 95 96
static void test_disconnected(struct knot_requestor *requestor,
                              const struct sockaddr_storage *dst,
                              const struct sockaddr_storage *src)
97
{
98
	struct knot_request *req = make_query(requestor, dst, src);
Jan Včelák's avatar
Jan Včelák committed
99 100 101
	int ret = knot_requestor_exec(requestor, req, TIMEOUT);
	is_int(KNOT_ECONN, ret, "requestor: disconnected/exec");
	knot_request_free(req, requestor->mm);
102 103 104

}

105 106 107
static void test_connected(struct knot_requestor *requestor,
                           const struct sockaddr_storage *dst,
                           const struct sockaddr_storage *src)
108 109
{
	/* Enqueue packet. */
110
	struct knot_request *req = make_query(requestor, dst, src);
Jan Včelák's avatar
Jan Včelák committed
111 112 113
	int ret = knot_requestor_exec(requestor, req, TIMEOUT);
	is_int(KNOT_EOK, ret, "requestor: connected/exec");
	knot_request_free(req, requestor->mm);
114 115 116 117
}

int main(int argc, char *argv[])
{
118
	plan_lazy();
119

120
	knot_mm_t mm;
121
	mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
122

123
	/* Initialize requestor. */
124
	struct knot_requestor requestor;
125
	knot_requestor_init(&requestor, &dummy_module, NULL, &mm);
126

127 128 129 130 131
	/* Define endpoints. */
	struct sockaddr_storage client = { 0 };
	sockaddr_set(&client, AF_INET, "127.0.0.1", 0);
	struct sockaddr_storage server = { 0 };
	sockaddr_set(&server, AF_INET, "127.0.0.1", 0);
132 133

	/* Bind to random port. */
134
	int responder_fd = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&server, 0);
135 136 137
	assert(responder_fd >= 0);
	socklen_t addr_len = sockaddr_len((struct sockaddr *)&server);
	getsockname(responder_fd, (struct sockaddr *)&server, &addr_len);
138

139 140 141 142 143
	/* Test requestor in disconnected environment. */
	test_disconnected(&requestor, &server, &client);

	/* Start responder. */
	int ret = listen(responder_fd, 10);
144
	(void)ret;
145
	assert(ret == 0);
146
	pthread_t thread;
147
	pthread_create(&thread, 0, responder_thread, &responder_fd);
148 149

	/* Test requestor in connected environment. */
150
	test_connected(&requestor, &server, &client);
151 152

	/* Terminate responder. */
153
	int conn = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&server, NULL);
154
	assert(conn > 0);
155 156
	conn = net_dns_tcp_send(conn, (uint8_t *)"", 1, TIMEOUT);
	assert(conn > 0);
157 158
	pthread_join(thread, NULL);
	close(responder_fd);
159 160 161 162 163 164

	/* Cleanup. */
	mp_delete((struct mempool *)mm.ctx);

	return 0;
}