1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <assert.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 
24 static ssize_t __read(int fd, void *buf, size_t len);
25 #define read __read
26 #include "config.c"
27 #include "console-socket.c"
28 #define main __main
29 #include "console-client.c"
30 #undef read
31 #undef main
32 
33 struct test {
34 	enum esc_type esc_type;
35 	union {
36 		struct ssh_esc_state ssh;
37 		struct str_esc_state str;
38 	} esc_state;
39 	const char *in[4];
40 	size_t n_in;
41 	const char *exp_out;
42 	int exp_rc;
43 };
44 
45 struct test_ctx {
46 	struct console_client client;
47 	struct test *test;
48 	uint8_t out[4096];
49 	size_t cur_in;
50 	size_t cur_out;
51 };
52 
53 struct test tests[] = {
54 	{
55 		/* no escape code */
56 		.esc_type = ESC_TYPE_SSH,
57 		.in = { "a" },
58 		.n_in = 1,
59 		.exp_out = "a",
60 		.exp_rc = PROCESS_EXIT,
61 	},
62 	{
63 		/* no escape code, multiple reads */
64 		.esc_type = ESC_TYPE_SSH,
65 		.in = { "a", "b" },
66 		.n_in = 2,
67 		.exp_out = "ab",
68 		.exp_rc = PROCESS_EXIT,
69 	},
70 	{
71 		/* ssh escape in one read */
72 		.esc_type = ESC_TYPE_SSH,
73 		.in = { "a\r~." },
74 		.n_in = 1,
75 		.exp_out = "a\r",
76 		.exp_rc = PROCESS_ESC,
77 	},
78 	{
79 		/* ssh escape, partial ~ is not output. */
80 		.esc_type = ESC_TYPE_SSH,
81 		.in = { "a\r~" },
82 		.n_in = 1,
83 		.exp_out = "a\r",
84 		.exp_rc = PROCESS_EXIT,
85 	},
86 	{
87 		/* ssh escape split into individual reads */
88 		.esc_type = ESC_TYPE_SSH,
89 		.in = { "a", "\r", "~", "." },
90 		.n_in = 4,
91 		.exp_out = "a\r",
92 		.exp_rc = PROCESS_ESC,
93 	},
94 	{
95 		/* ssh escape, escaped. */
96 		.esc_type = ESC_TYPE_SSH,
97 		.in = { "a\r~~." },
98 		.n_in = 1,
99 		.exp_out = "a\r~.",
100 		.exp_rc = PROCESS_EXIT,
101 	},
102 	{
103 		/* ssh escape, escaped ~, and not completed. */
104 		.esc_type = ESC_TYPE_SSH,
105 		.in = { "a\r~~" },
106 		.n_in = 1,
107 		.exp_out = "a\r~",
108 		.exp_rc = PROCESS_EXIT,
109 	},
110 	{
111 		/* str escape, no match */
112 		.esc_type = ESC_TYPE_STR,
113 		.esc_state = { .str = { .str = (const uint8_t *)"c" } },
114 		.in = { "ab" },
115 		.n_in = 1,
116 		.exp_out = "ab",
117 		.exp_rc = PROCESS_EXIT,
118 	},
119 	{
120 		/* str escape, one byte, as one read */
121 		.esc_type = ESC_TYPE_STR,
122 		.esc_state = { .str = { .str = (const uint8_t *)"b" } },
123 		.in = { "abc" },
124 		.n_in = 1,
125 		.exp_out = "ab",
126 		.exp_rc = PROCESS_ESC,
127 	},
128 	{
129 		/* str escape, multiple bytes, as one read */
130 		.esc_type = ESC_TYPE_STR,
131 		.esc_state = { .str = { .str = (const uint8_t *)"bc" } },
132 		.in = { "abcd" },
133 		.n_in = 1,
134 		.exp_out = "abc",
135 		.exp_rc = PROCESS_ESC,
136 	},
137 	{
138 		/* str escape, multiple bytes, split over reads */
139 		.esc_type = ESC_TYPE_STR,
140 		.esc_state = { .str = { .str = (const uint8_t *)"bc" } },
141 		.in = { "ab", "cd" },
142 		.n_in = 2,
143 		.exp_out = "abc",
144 		.exp_rc = PROCESS_ESC,
145 	},
146 	{
147 		/* str escape, not matched due to intermediate data */
148 		.esc_type = ESC_TYPE_STR,
149 		.esc_state = { .str = { .str = (const uint8_t *)"ab" } },
150 		.in = { "acb" },
151 		.n_in = 1,
152 		.exp_out = "acb",
153 		.exp_rc = PROCESS_EXIT,
154 	},
155 };
156 
157 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
158 
159 struct test_ctx ctxs[ARRAY_SIZE(tests)];
160 
write_buf_to_fd(int fd,const uint8_t * buf,size_t len)161 int write_buf_to_fd(int fd, const uint8_t *buf, size_t len)
162 {
163 	struct test_ctx *ctx = &ctxs[fd];
164 
165 	assert(ctx->cur_out + len <= sizeof(ctx->out));
166 	memcpy(ctx->out + ctx->cur_out, buf, len);
167 	ctx->cur_out += len;
168 
169 	return 0;
170 }
171 
__read(int fd,void * buf,size_t len)172 static ssize_t __read(int fd, void *buf, size_t len)
173 {
174 	struct test_ctx *ctx = &ctxs[fd];
175 	const char *inbuf;
176 	size_t inlen;
177 
178 	if (len > SSIZE_MAX) {
179 		errno = EINVAL;
180 		return -1;
181 	}
182 
183 	if (ctx->cur_in >= ctx->test->n_in) {
184 		return 0;
185 	}
186 
187 	inbuf = ctx->test->in[ctx->cur_in];
188 	inlen = strlen(inbuf);
189 	assert(inlen <= len);
190 	memcpy(buf, inbuf, inlen);
191 	ctx->cur_in++;
192 	return (ssize_t)inlen;
193 }
194 
run_one_test(size_t idx,struct test * test,struct test_ctx * ctx)195 void run_one_test(size_t idx, struct test *test, struct test_ctx *ctx)
196 {
197 	size_t exp_out_len;
198 	int rc;
199 
200 	/* we store the index into the context array as a FD, so we
201 	 * can refer to it through the read & write callbacks.
202 	 */
203 	assert(idx < INT_MAX);
204 	ctx->client.console_sd = (int)idx;
205 	ctx->client.fd_in = (int)idx;
206 	ctx->client.esc_type = test->esc_type;
207 	memcpy(&ctx->client.esc_state, &test->esc_state,
208 	       sizeof(test->esc_state));
209 	ctx->test = test;
210 
211 	for (;;) {
212 		rc = process_tty(&ctx->client);
213 		if (rc != PROCESS_OK) {
214 			break;
215 		}
216 	}
217 
218 	exp_out_len = strlen(test->exp_out);
219 
220 #ifdef DEBUG
221 	printf("got: rc %d %s(%d), exp: rc %d %s(%ld)\n", rc, ctx->out,
222 	       ctx->cur_out, test->exp_rc, test->exp_out, exp_out_len);
223 	fflush(stdout);
224 #endif
225 	assert(rc == test->exp_rc);
226 	assert(exp_out_len == ctx->cur_out);
227 	assert(!memcmp(ctx->out, test->exp_out, exp_out_len));
228 }
229 
main(void)230 int main(void)
231 {
232 	size_t i;
233 
234 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
235 		run_one_test(i, &tests[i], &ctxs[i]);
236 	}
237 
238 	return EXIT_SUCCESS;
239 }
240