1 /*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 #include <tap/basic.h>
22 #include <tap/files.h>
23 
24 #define CTL_BUFF_SIZE	18
25 #include "libknot/control/control.c"
26 
27 #define fake_ok(condition, msg, ...) \
28 	if (!(condition)) { \
29 		if (msg != NULL) { \
30 			printf("error: " msg "\n", ##__VA_ARGS__); \
31 		} \
32 		exit(-1); \
33 	}
34 
ctl_client(const char * socket,size_t argc,knot_ctl_data_t * argv)35 static void ctl_client(const char *socket, size_t argc, knot_ctl_data_t *argv)
36 {
37 	knot_ctl_t *ctl = knot_ctl_alloc();
38 	fake_ok(ctl != NULL, "Allocate control");
39 
40 	int ret;
41 	for (int i = 0; i < 20; i++) {
42 		ret = knot_ctl_connect(ctl, socket);
43 		if (ret == KNOT_EOK) {
44 			break;
45 		}
46 		usleep(100000);
47 	}
48 	fake_ok(ret == KNOT_EOK, "Connect to socket");
49 
50 	diag("BEGIN: Client -> Server");
51 
52 	if (argc > 0) {
53 		for (size_t i = 0; i < argc; i++) {
54 			if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
55 			    argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
56 				ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
57 				fake_ok(ret == KNOT_EOK, "Client send data block end type");
58 			} else {
59 				ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
60 				fake_ok(ret == KNOT_EOK, "Client send data %zu", i);
61 			}
62 		}
63 	}
64 
65 	ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
66 	fake_ok(ret == KNOT_EOK, "Client send final data");
67 
68 	diag("END: Client -> Server");
69 	diag("BEGIN: Client <- Server");
70 
71 	size_t count = 0;
72 	knot_ctl_data_t data;
73 	knot_ctl_type_t type = KNOT_CTL_TYPE_DATA;
74 	while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
75 		if (type == KNOT_CTL_TYPE_END) {
76 			break;
77 		}
78 		if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
79 		    argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
80 			fake_ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
81 		} else {
82 			fake_ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
83 			for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
84 				fake_ok((data[i] == NULL && argv[count][i] == NULL) ||
85 					(data[i] != NULL && argv[count][i] != NULL),
86 					"Client compare input item occupation %zu", i);
87 				if (data[i] == NULL) {
88 					continue;
89 				}
90 
91 				fake_ok(strcmp(data[i], argv[count][i]) == 0,
92 					"Client compare input item '%s", argv[count][i]);
93 			}
94 		}
95 		count++;
96 	}
97 	fake_ok(ret == KNOT_EOK, "Receive OK check");
98 	fake_ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
99 	fake_ok(count == argc, "Client compare input count '%zu'", argc);
100 
101 	diag("END: Client <- Server");
102 
103 	knot_ctl_close(ctl);
104 	knot_ctl_free(ctl);
105 }
106 
ctl_server(const char * socket,size_t argc,knot_ctl_data_t * argv)107 static void ctl_server(const char *socket, size_t argc, knot_ctl_data_t *argv)
108 {
109 	knot_ctl_t *ctl = knot_ctl_alloc();
110 	ok(ctl != NULL, "Allocate control");
111 
112 	int ret = knot_ctl_bind(ctl, socket);
113 	is_int(KNOT_EOK, ret, "Bind control socket");
114 
115 	ret = knot_ctl_accept(ctl);
116 	is_int(KNOT_EOK, ret, "Accept a connection");
117 
118 	diag("BEGIN: Server <- Client");
119 
120 	size_t count = 0;
121 	knot_ctl_data_t data;
122 	knot_ctl_type_t type = KNOT_CTL_TYPE_DATA;
123 	while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
124 		if (type == KNOT_CTL_TYPE_END) {
125 			break;
126 		}
127 		if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
128 		    argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
129 			ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
130 		} else {
131 			ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
132 			for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
133 				ok((data[i] == NULL && argv[count][i] == NULL) ||
134 				   (data[i] != NULL && argv[count][i] != NULL),
135 				   "Server compare input item occupation %zu", i);
136 				if (data[i] == NULL) {
137 					continue;
138 				}
139 
140 				ok(strcmp(data[i], argv[count][i]) == 0,
141 				   "Server compare input item '%s", argv[count][i]);
142 			}
143 		}
144 		count++;
145 	}
146 	is_int(KNOT_EOK, ret, "Receive OK check");
147 	ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
148 	ok(count == argc, "Server compare input count '%zu'", argc);
149 
150 	diag("END: Server <- Client");
151 	diag("BEGIN: Server -> Client");
152 
153 	if (argc > 0) {
154 		for (size_t i = 0; i < argc; i++) {
155 			if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
156 			    argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
157 				ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
158 				is_int(KNOT_EOK, ret, "Client send data block end type");
159 			} else {
160 				ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
161 				is_int(KNOT_EOK, ret, "Server send data %zu", i);
162 			}
163 		}
164 	}
165 
166 	ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
167 	is_int(KNOT_EOK, ret, "Server send final data");
168 
169 	diag("END: Server -> Client");
170 
171 	knot_ctl_close(ctl);
172 	knot_ctl_unbind(ctl);
173 	knot_ctl_free(ctl);
174 }
175 
test_client_server_client(void)176 static void test_client_server_client(void)
177 {
178 	char *socket = test_mktemp();
179 	ok(socket != NULL, "Make a temporary socket file '%s'", socket);
180 
181 	size_t data_len = 5;
182 	knot_ctl_data_t data[] = {
183 		{ "command", "error", "section", "item", "identifier",
184 		  "zone", "owner", "ttl", "type", "data" },
185 		{ [KNOT_CTL_IDX_DATA] = "\x01\x02" },
186 		{ [KNOT_CTL_IDX_CMD] = "\0" }, // This means block end in this test!
187 		{ NULL },
188 		{ [KNOT_CTL_IDX_ERROR] = "Ultra long message" }
189 	};
190 
191 	// Fork a client process.
192 	pid_t child_pid = fork();
193 	if (child_pid == -1) {
194 		ok(child_pid >= 0, "Process fork");
195 		return;
196 	}
197 	if (child_pid == 0) {
198 		ctl_client(socket, data_len, data);
199 		free(socket);
200 		return;
201 	} else {
202 		ctl_server(socket, data_len, data);
203 	}
204 
205 	int status = 0;
206 	wait(&status);
207 	ok(WIFEXITED(status), "Wait for client");
208 
209 	test_rm_rf(socket);
210 	free(socket);
211 }
212 
main(int argc,char * argv[])213 int main(int argc, char *argv[])
214 {
215 	plan_lazy();
216 
217 	diag("Client -> Server -> Client");
218 	test_client_server_client();
219 
220 	return 0;
221 }
222