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