-
Notifications
You must be signed in to change notification settings - Fork 1
/
ish2.c
130 lines (100 loc) · 2.98 KB
/
ish2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
* ish2.c: an HTTP/2 support check tool via ALPN.
*/
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#define TARGET_SIZE 1024
const unsigned char protos[] = {2, 'h', '2', 8, 'h', 't', 't', 'p', '/', '1', '.', '1'};
void exit_program(const char* host, const char* msg, SSL_CTX* ctx, BIO* bio) {
fprintf(stderr, "%s: %s\n", host, msg ? msg : ERR_error_string(ERR_get_error(), NULL));
BIO_free_all(bio);
SSL_CTX_free(ctx);
exit(EXIT_FAILURE);
}
static void __strtrunc(char* str, char c) {
char* ptr = strchr(str, c);
if (ptr)
*ptr = '\0';
}
int parse_args(char* target, size_t target_size, const char* host, const char* port) {
char* ptr;
/* Remove protocol scheme if present */
ptr = strstr(host, "://");
if (ptr) {
if (ptr[3] == '\0')
return -1;
strncpy(target, ptr + 3, target_size - 1);
} else {
strncpy(target, host, target_size - 1);
}
/* Remove web path and port if present */
__strtrunc(target, '/');
__strtrunc(target, ':');
/* Check for overflow and write final target string */
if ((strlen(target) + strlen(port) + 2) > target_size)
return -1;
strcat(strcat(target, ":"), port);
return 0;
}
int main(int argc, char *argv[]) {
char *port;
char target[TARGET_SIZE] = {0};
char hostname[TARGET_SIZE] = {0};
const unsigned char* alpn_proto;
unsigned int alpn_length;
const SSL_METHOD* method;
SSL_CTX* ctx = NULL;
BIO* bio = NULL;
SSL* ssl = NULL;
if (argc < 2)
errx(EXIT_SUCCESS, "%s <host> [port]", argv[0]);
if (argc > 2) {
port = argv[2];
} else {
port = "443";
}
if (parse_args(target, TARGET_SIZE, argv[1], port) < 0)
exit_program(argv[1], "error: input too long or invalid format", ctx, bio);
/* Copy target without port into hostname */
strncpy(hostname, target, TARGET_SIZE - 1);
__strtrunc(hostname, ':');
/* Set up SSL method and context */
method = TLS_client_method();
if (method == NULL)
exit_program(hostname, NULL, ctx, bio);
ctx = SSL_CTX_new(method);
if (ctx == NULL)
exit_program(hostname, NULL, ctx, bio);
#ifdef NVERIFY
/* Do not verify certificates */
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
#endif
/* Set ALPN */
if (SSL_CTX_set_alpn_protos(ctx, protos, sizeof(protos)) != 0)
exit_program(hostname, NULL, ctx, bio);
bio = BIO_new_ssl_connect(ctx);
if (bio == NULL)
exit_program(hostname, NULL, ctx, bio);
/* Set hostname */
if (BIO_set_conn_hostname(bio, target) != 1)
exit_program(hostname, NULL, ctx, bio);
/* Set SNI */
if (BIO_get_ssl(bio, &ssl) <= 0 || SSL_set_tlsext_host_name(ssl, hostname) != 1)
exit_program(hostname, NULL, ctx, bio);
/* Connect */
if (BIO_do_connect(bio) != 1 || BIO_do_handshake(bio) != 1)
exit_program(hostname, NULL, ctx, bio);
/* Print result */
SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_length);
if (alpn_proto != NULL) {
printf("%s %.*s\n", hostname, alpn_length, alpn_proto);
} else {
printf("%s http/1.1\n", hostname);
}
SSL_CTX_free(ctx);
BIO_free_all(bio);
return EXIT_SUCCESS;
}