1 /* Copyright (c) 2013-2016, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #define CONNECTION_PRIVATE
5 #define EXT_ORPORT_PRIVATE
9 #include "connection.h"
10 #include "connection_or.h"
13 #include "ext_orport.h"
17 /* Test connection_or_remove_from_ext_or_id_map and
18 * connection_or_set_ext_or_identifier */
20 test_ext_or_id_map(void *arg
)
22 or_connection_t
*c1
= NULL
, *c2
= NULL
, *c3
= NULL
;
23 char *idp
= NULL
, *idp2
= NULL
;
26 /* pre-initialization */
27 tt_ptr_op(NULL
, OP_EQ
,
28 connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
30 c1
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
31 c2
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
32 c3
= or_connection_new(CONN_TYPE_OR
, AF_INET
);
34 tt_ptr_op(c1
->ext_or_conn_id
, OP_NE
, NULL
);
35 tt_ptr_op(c2
->ext_or_conn_id
, OP_NE
, NULL
);
36 tt_ptr_op(c3
->ext_or_conn_id
, OP_EQ
, NULL
);
38 tt_ptr_op(c1
, OP_EQ
, connection_or_get_by_ext_or_id(c1
->ext_or_conn_id
));
39 tt_ptr_op(c2
, OP_EQ
, connection_or_get_by_ext_or_id(c2
->ext_or_conn_id
));
40 tt_ptr_op(NULL
, OP_EQ
,
41 connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
43 idp
= tor_memdup(c2
->ext_or_conn_id
, EXT_OR_CONN_ID_LEN
);
45 /* Give c2 a new ID. */
46 connection_or_set_ext_or_identifier(c2
);
47 tt_mem_op(idp
, OP_NE
, c2
->ext_or_conn_id
, EXT_OR_CONN_ID_LEN
);
48 idp2
= tor_memdup(c2
->ext_or_conn_id
, EXT_OR_CONN_ID_LEN
);
49 tt_assert(!tor_digest_is_zero(idp2
));
51 tt_ptr_op(NULL
, OP_EQ
, connection_or_get_by_ext_or_id(idp
));
52 tt_ptr_op(c2
, OP_EQ
, connection_or_get_by_ext_or_id(idp2
));
55 connection_or_remove_from_ext_or_id_map(c2
);
56 tt_ptr_op(NULL
, OP_EQ
, connection_or_get_by_ext_or_id(idp
));
57 tt_ptr_op(NULL
, OP_EQ
, connection_or_get_by_ext_or_id(idp2
));
61 connection_free_(TO_CONN(c1
));
63 connection_free_(TO_CONN(c2
));
65 connection_free_(TO_CONN(c3
));
68 connection_or_clear_ext_or_id_map();
71 /* Simple connection_write_to_buf_impl_ replacement that unconditionally
72 * writes to outbuf. */
74 connection_write_to_buf_impl_replacement(const char *string
, size_t len
,
75 connection_t
*conn
, int zlib
)
81 write_to_buf(string
, len
, conn
->outbuf
);
85 buf_get_contents(buf_t
*buf
, size_t *sz_out
)
88 *sz_out
= buf_datalen(buf
);
89 if (*sz_out
>= ULONG_MAX
)
90 return NULL
; /* C'mon, really? */
91 out
= tor_malloc(*sz_out
+ 1);
92 if (fetch_from_buf(out
, (unsigned long)*sz_out
, buf
) != 0) {
96 out
[*sz_out
] = '\0'; /* Hopefully gratuitous. */
101 test_ext_or_write_command(void *arg
)
109 MOCK(connection_write_to_buf_impl_
,
110 connection_write_to_buf_impl_replacement
);
112 c1
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
115 /* Length too long */
116 tt_int_op(connection_write_ext_or_command(TO_CONN(c1
), 100, "X", 100000),
120 tt_int_op(connection_write_ext_or_command(TO_CONN(c1
), 0x99, NULL
, 0),
122 cp
= buf_get_contents(TO_CONN(c1
)->outbuf
, &sz
);
123 tt_int_op(sz
, OP_EQ
, 4);
124 tt_mem_op(cp
, OP_EQ
, "\x00\x99\x00\x00", 4);
127 /* Medium command. */
128 tt_int_op(connection_write_ext_or_command(TO_CONN(c1
), 0x99,
129 "Wai\0Hello", 9), OP_EQ
, 0);
130 cp
= buf_get_contents(TO_CONN(c1
)->outbuf
, &sz
);
131 tt_int_op(sz
, OP_EQ
, 13);
132 tt_mem_op(cp
, OP_EQ
, "\x00\x99\x00\x09Wai\x00Hello", 13);
136 buf
= tor_malloc(65535);
137 memset(buf
, 'x', 65535);
138 tt_int_op(connection_write_ext_or_command(TO_CONN(c1
), 0xf00d,
139 buf
, 65535), OP_EQ
, 0);
140 cp
= buf_get_contents(TO_CONN(c1
)->outbuf
, &sz
);
141 tt_int_op(sz
, OP_EQ
, 65539);
142 tt_mem_op(cp
, OP_EQ
, "\xf0\x0d\xff\xff", 4);
143 tt_mem_op(cp
+4, OP_EQ
, buf
, 65535);
148 connection_free_(TO_CONN(c1
));
151 UNMOCK(connection_write_to_buf_impl_
);
155 write_bytes_to_file_fail(const char *fname
, const char *str
, size_t len
,
167 test_ext_or_init_auth(void *arg
)
169 or_options_t
*options
= get_options_mutable();
176 /* Check default filename location */
177 tor_free(options
->DataDirectory
);
178 options
->DataDirectory
= tor_strdup("foo");
179 cp
= get_ext_or_auth_cookie_file_name();
180 tt_str_op(cp
, OP_EQ
, "foo"PATH_SEPARATOR
"extended_orport_auth_cookie");
183 /* Shouldn't be initialized already, or our tests will be a bit
185 ext_or_auth_cookie
= tor_malloc_zero(32);
186 tt_assert(tor_mem_is_zero((char*)ext_or_auth_cookie
, 32));
188 /* Now make sure we use a temporary file */
189 fn
= get_fname("ext_cookie_file");
190 options
->ExtORPortCookieAuthFile
= tor_strdup(fn
);
191 cp
= get_ext_or_auth_cookie_file_name();
192 tt_str_op(cp
, OP_EQ
, fn
);
195 /* Test the initialization function with a broken
196 write_bytes_to_file(). See if the problem is handled properly. */
197 MOCK(write_bytes_to_file
, write_bytes_to_file_fail
);
198 tt_int_op(-1, OP_EQ
, init_ext_or_cookie_authentication(1));
199 tt_int_op(ext_or_auth_cookie_is_set
, OP_EQ
, 0);
200 UNMOCK(write_bytes_to_file
);
202 /* Now do the actual initialization. */
203 tt_int_op(0, OP_EQ
, init_ext_or_cookie_authentication(1));
204 tt_int_op(ext_or_auth_cookie_is_set
, OP_EQ
, 1);
205 cp
= read_file_to_str(fn
, RFTS_BIN
, &st
);
206 tt_ptr_op(cp
, OP_NE
, NULL
);
207 tt_u64_op((uint64_t)st
.st_size
, OP_EQ
, 64);
208 tt_mem_op(cp
,OP_EQ
, "! Extended ORPort Auth Cookie !\x0a", 32);
209 tt_mem_op(cp
+32,OP_EQ
, ext_or_auth_cookie
, 32);
210 memcpy(cookie0
, ext_or_auth_cookie
, 32);
211 tt_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie
, 32));
213 /* Operation should be idempotent. */
214 tt_int_op(0, OP_EQ
, init_ext_or_cookie_authentication(1));
215 tt_mem_op(cookie0
,OP_EQ
, ext_or_auth_cookie
, 32);
219 ext_orport_free_all();
223 test_ext_or_cookie_auth(void *arg
)
225 char *reply
=NULL
, *reply2
=NULL
, *client_hash
=NULL
, *client_hash2
=NULL
;
227 char hmac1
[32], hmac2
[32];
229 const char client_nonce
[32] =
230 "Who is the third who walks alway";
231 char server_hash_input
[] =
232 "ExtORPort authentication server-to-client hash"
233 "Who is the third who walks alway"
234 "................................";
235 char client_hash_input
[] =
236 "ExtORPort authentication client-to-server hash"
237 "Who is the third who walks alway"
238 "................................";
242 tt_int_op(strlen(client_hash_input
), OP_EQ
, 46+32+32);
243 tt_int_op(strlen(server_hash_input
), OP_EQ
, 46+32+32);
245 ext_or_auth_cookie
= tor_malloc_zero(32);
246 memcpy(ext_or_auth_cookie
, "s beside you? When I count, ther", 32);
247 ext_or_auth_cookie_is_set
= 1;
249 /* For this authentication, the client sends 32 random bytes (ClientNonce)
250 * The server replies with 32 byte ServerHash and 32 byte ServerNonce,
251 * where ServerHash is:
252 * HMAC-SHA256(CookieString,
253 * "ExtORPort authentication server-to-client hash" | ClientNonce |
255 * The client must reply with 32-byte ClientHash, which we compute as:
256 * ClientHash is computed as:
257 * HMAC-SHA256(CookieString,
258 * "ExtORPort authentication client-to-server hash" | ClientNonce |
264 handle_client_auth_nonce(client_nonce
, 33, &client_hash
, &reply
,
267 handle_client_auth_nonce(client_nonce
, 31, &client_hash
, &reply
,
270 /* Now let's try this for real! */
272 handle_client_auth_nonce(client_nonce
, 32, &client_hash
, &reply
,
274 tt_int_op(reply_len
, OP_EQ
, 64);
275 tt_ptr_op(reply
, OP_NE
, NULL
);
276 tt_ptr_op(client_hash
, OP_NE
, NULL
);
277 /* Fill in the server nonce into the hash inputs... */
278 memcpy(server_hash_input
+46+32, reply
+32, 32);
279 memcpy(client_hash_input
+46+32, reply
+32, 32);
280 /* Check the HMACs are correct... */
281 crypto_hmac_sha256(hmac1
, (char*)ext_or_auth_cookie
, 32, server_hash_input
,
283 crypto_hmac_sha256(hmac2
, (char*)ext_or_auth_cookie
, 32, client_hash_input
,
285 tt_mem_op(hmac1
,OP_EQ
, reply
, 32);
286 tt_mem_op(hmac2
,OP_EQ
, client_hash
, 32);
288 /* Now do it again and make sure that the results are *different* */
290 handle_client_auth_nonce(client_nonce
, 32, &client_hash2
, &reply2
,
292 tt_mem_op(reply2
,OP_NE
, reply
, reply_len
);
293 tt_mem_op(client_hash2
,OP_NE
, client_hash
, 32);
294 /* But that this one checks out too. */
295 memcpy(server_hash_input
+46+32, reply2
+32, 32);
296 memcpy(client_hash_input
+46+32, reply2
+32, 32);
297 /* Check the HMACs are correct... */
298 crypto_hmac_sha256(hmac1
, (char*)ext_or_auth_cookie
, 32, server_hash_input
,
300 crypto_hmac_sha256(hmac2
, (char*)ext_or_auth_cookie
, 32, client_hash_input
,
302 tt_mem_op(hmac1
,OP_EQ
, reply2
, 32);
303 tt_mem_op(hmac2
,OP_EQ
, client_hash2
, 32);
307 tor_free(client_hash
);
309 tor_free(client_hash2
);
313 crypto_rand_return_tse_str(char *to
, size_t n
)
316 TT_FAIL(("Asked for %d bytes, not 32", (int)n
));
319 memcpy(to
, "te road There is always another ", 32);
323 test_ext_or_cookie_auth_testvec(void *arg
)
325 char *reply
=NULL
, *client_hash
=NULL
;
327 char *mem_op_hex_tmp
=NULL
;
329 const char client_nonce
[] = "But when I look ahead up the whi";
332 ext_or_auth_cookie
= tor_malloc_zero(32);
333 memcpy(ext_or_auth_cookie
, "Gliding wrapt in a brown mantle," , 32);
334 ext_or_auth_cookie_is_set
= 1;
336 MOCK(crypto_rand
, crypto_rand_return_tse_str
);
339 handle_client_auth_nonce(client_nonce
, 32, &client_hash
, &reply
,
341 tt_ptr_op(reply
, OP_NE
, NULL
);
342 tt_uint_op(reply_len
, OP_EQ
, 64);
343 tt_mem_op(reply
+32,OP_EQ
, "te road There is always another ", 32);
344 /* HMACSHA256("Gliding wrapt in a brown mantle,"
345 * "ExtORPort authentication server-to-client hash"
346 * "But when I look ahead up the write road There is always another ");
348 test_memeq_hex(reply
,
349 "ec80ed6e546d3b36fdfc22fe1315416b"
350 "029f1ade7610d910878b62eeb7403821");
351 /* HMACSHA256("Gliding wrapt in a brown mantle,"
352 * "ExtORPort authentication client-to-server hash"
353 * "But when I look ahead up the write road There is always another ");
354 * (Both values computed using Python CLI.)
356 test_memeq_hex(client_hash
,
357 "ab391732dd2ed968cd40c087d1b1f25b"
358 "33b3cd77ff79bd80c2074bbf438119a2");
363 tor_free(client_hash
);
364 tor_free(mem_op_hex_tmp
);
368 ignore_bootstrap_problem(const char *warn
, int reason
,
369 or_connection_t
*conn
)
376 static int is_reading
= 1;
377 static int handshake_start_called
= 0;
380 note_read_stopped(connection_t
*conn
)
386 note_read_started(connection_t
*conn
)
392 handshake_start(or_connection_t
*conn
, int receiving
)
394 if (!conn
|| !receiving
)
395 TT_FAIL(("Bad arguments to handshake_start"));
396 handshake_start_called
= 1;
402 write_to_buf((s), (n), TO_CONN(conn)->inbuf); \
404 #define CONTAINS(s,n) \
406 tt_int_op((n), OP_LE, sizeof(b)); \
407 tt_int_op(buf_datalen(TO_CONN(conn)->outbuf), OP_EQ, (n)); \
409 fetch_from_buf(b, (n), TO_CONN(conn)->outbuf); \
410 tt_mem_op(b, OP_EQ, (s), (n)); \
414 /* Helper: Do a successful Extended ORPort authentication handshake. */
416 do_ext_or_handshake(or_connection_t
*conn
)
420 tt_int_op(0, OP_EQ
, connection_ext_or_start_auth(conn
));
421 CONTAINS("\x01\x00", 2);
423 WRITE("But when I look ahead up the whi", 32);
424 MOCK(crypto_rand
, crypto_rand_return_tse_str
);
425 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
427 tt_int_op(TO_CONN(conn
)->state
, OP_EQ
,
428 EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH
);
429 CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
430 "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
431 "te road There is always another ", 64);
432 /* Send the right response this time. */
433 WRITE("\xab\x39\x17\x32\xdd\x2e\xd9\x68\xcd\x40\xc0\x87\xd1\xb1\xf2\x5b"
434 "\x33\xb3\xcd\x77\xff\x79\xbd\x80\xc2\x07\x4b\xbf\x43\x81\x19\xa2",
436 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
438 tt_assert(! TO_CONN(conn
)->marked_for_close
);
439 tt_int_op(TO_CONN(conn
)->state
, OP_EQ
, EXT_OR_CONN_STATE_OPEN
);
445 test_ext_or_handshake(void *arg
)
447 or_connection_t
*conn
=NULL
;
451 MOCK(connection_write_to_buf_impl_
,
452 connection_write_to_buf_impl_replacement
);
453 /* Use same authenticators as for test_ext_or_cookie_auth_testvec */
454 ext_or_auth_cookie
= tor_malloc_zero(32);
455 memcpy(ext_or_auth_cookie
, "Gliding wrapt in a brown mantle," , 32);
456 ext_or_auth_cookie_is_set
= 1;
458 init_connection_lists();
460 conn
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
461 tt_int_op(0, OP_EQ
, connection_ext_or_start_auth(conn
));
462 /* The server starts by telling us about the one supported authtype. */
463 CONTAINS("\x01\x00", 2);
464 /* Say the client hasn't responded yet. */
465 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
466 /* Let's say the client replies badly. */
468 tt_int_op(-1, OP_EQ
, connection_ext_or_process_inbuf(conn
));
470 tt_assert(TO_CONN(conn
)->marked_for_close
);
471 close_closeable_connections();
474 /* Okay, try again. */
475 conn
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
476 tt_int_op(0, OP_EQ
, connection_ext_or_start_auth(conn
));
477 CONTAINS("\x01\x00", 2);
478 /* Let's say the client replies sensibly this time. "Yes, AUTHTYPE_COOKIE
479 * sounds delicious. Let's have some of that!" */
481 /* Let's say that the client also sends part of a nonce. */
482 WRITE("But when I look ", 16);
483 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
485 tt_int_op(TO_CONN(conn
)->state
, OP_EQ
,
486 EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE
);
487 /* Pump it again. Nothing should happen. */
488 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
489 /* send the rest of the nonce. */
490 WRITE("ahead up the whi", 16);
491 MOCK(crypto_rand
, crypto_rand_return_tse_str
);
492 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
494 /* We should get the right reply from the server. */
495 CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
496 "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
497 "te road There is always another ", 64);
498 /* Send the wrong response. */
499 WRITE("not with a bang but a whimper...", 32);
500 MOCK(control_event_bootstrap_problem
, ignore_bootstrap_problem
);
501 tt_int_op(-1, OP_EQ
, connection_ext_or_process_inbuf(conn
));
503 tt_assert(TO_CONN(conn
)->marked_for_close
);
504 /* XXXX Hold-open-until-flushed. */
505 close_closeable_connections();
507 UNMOCK(control_event_bootstrap_problem
);
509 MOCK(connection_start_reading
, note_read_started
);
510 MOCK(connection_stop_reading
, note_read_stopped
);
511 MOCK(connection_tls_start_handshake
, handshake_start
);
513 /* Okay, this time let's succeed. */
514 conn
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
515 do_ext_or_handshake(conn
);
517 /* Now let's run through some messages. */
518 /* First let's send some junk and make sure it's ignored. */
519 WRITE("\xff\xf0\x00\x03""ABC", 7);
520 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
522 /* Now let's send a USERADDR command. */
523 WRITE("\x00\x01\x00\x0c""1.2.3.4:5678", 16);
524 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
525 tt_int_op(TO_CONN(conn
)->port
, OP_EQ
, 5678);
526 tt_int_op(tor_addr_to_ipv4h(&TO_CONN(conn
)->addr
), OP_EQ
, 0x01020304);
527 /* Now let's send a TRANSPORT command. */
528 WRITE("\x00\x02\x00\x07""rfc1149", 11);
529 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
530 tt_ptr_op(NULL
, OP_NE
, conn
->ext_or_transport
);
531 tt_str_op("rfc1149", OP_EQ
, conn
->ext_or_transport
);
532 tt_int_op(is_reading
,OP_EQ
,1);
533 tt_int_op(TO_CONN(conn
)->state
, OP_EQ
, EXT_OR_CONN_STATE_OPEN
);
535 WRITE("\x00\x00\x00\x00", 4);
536 tt_int_op(0, OP_EQ
, connection_ext_or_process_inbuf(conn
));
537 tt_int_op(TO_CONN(conn
)->state
, OP_EQ
, EXT_OR_CONN_STATE_FLUSHING
);
538 tt_int_op(is_reading
,OP_EQ
,0);
539 CONTAINS("\x10\x00\x00\x00", 4);
540 tt_int_op(handshake_start_called
,OP_EQ
,0);
541 tt_int_op(0, OP_EQ
, connection_ext_or_finished_flushing(conn
));
542 tt_int_op(is_reading
,OP_EQ
,1);
543 tt_int_op(handshake_start_called
,OP_EQ
,1);
544 tt_int_op(TO_CONN(conn
)->type
, OP_EQ
, CONN_TYPE_OR
);
545 tt_int_op(TO_CONN(conn
)->state
, OP_EQ
, 0);
546 close_closeable_connections();
549 /* Okay, this time let's succeed the handshake but fail the USERADDR
551 conn
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
552 do_ext_or_handshake(conn
);
553 /* USERADDR command with an extra NUL byte */
554 WRITE("\x00\x01\x00\x0d""1.2.3.4:5678\x00", 17);
555 MOCK(control_event_bootstrap_problem
, ignore_bootstrap_problem
);
556 tt_int_op(-1, OP_EQ
, connection_ext_or_process_inbuf(conn
));
558 tt_assert(TO_CONN(conn
)->marked_for_close
);
559 close_closeable_connections();
561 UNMOCK(control_event_bootstrap_problem
);
563 /* Now fail the TRANSPORT command. */
564 conn
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
565 do_ext_or_handshake(conn
);
566 /* TRANSPORT command with an extra NUL byte */
567 WRITE("\x00\x02\x00\x08""rfc1149\x00", 12);
568 MOCK(control_event_bootstrap_problem
, ignore_bootstrap_problem
);
569 tt_int_op(-1, OP_EQ
, connection_ext_or_process_inbuf(conn
));
571 tt_assert(TO_CONN(conn
)->marked_for_close
);
572 close_closeable_connections();
574 UNMOCK(control_event_bootstrap_problem
);
576 /* Now fail the TRANSPORT command. */
577 conn
= or_connection_new(CONN_TYPE_EXT_OR
, AF_INET
);
578 do_ext_or_handshake(conn
);
579 /* TRANSPORT command with transport name with symbols (not a
581 WRITE("\x00\x02\x00\x07""rf*1149", 11);
582 MOCK(control_event_bootstrap_problem
, ignore_bootstrap_problem
);
583 tt_int_op(-1, OP_EQ
, connection_ext_or_process_inbuf(conn
));
585 tt_assert(TO_CONN(conn
)->marked_for_close
);
586 close_closeable_connections();
588 UNMOCK(control_event_bootstrap_problem
);
591 UNMOCK(connection_write_to_buf_impl_
);
594 connection_free_(TO_CONN(conn
));
599 struct testcase_t extorport_tests
[] = {
600 { "id_map", test_ext_or_id_map
, TT_FORK
, NULL
, NULL
},
601 { "write_command", test_ext_or_write_command
, TT_FORK
, NULL
, NULL
},
602 { "init_auth", test_ext_or_init_auth
, TT_FORK
, NULL
, NULL
},
603 { "cookie_auth", test_ext_or_cookie_auth
, TT_FORK
, NULL
, NULL
},
604 { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec
, TT_FORK
,
606 { "handshake", test_ext_or_handshake
, TT_FORK
, NULL
, NULL
},