2 * Simple GnuTLS client used for testing.
4 * Copyright (C) 2011-2014 Simon Ruderich
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <arpa/inet.h>
30 #include <sys/socket.h>
31 #include <sys/types.h>
34 #include <gnutls/gnutls.h>
35 #include <gnutls/x509.h>
38 #define MAX_REQUEST_LINE 4096
40 static int fdopen_read_write(int socket, FILE **read_fd, FILE **write_fd);
41 static int connect_to_host(const char *hostname, const char *port);
42 static int read_http_request(FILE *client_fd, char *request, size_t length);
45 static void log_function_gnutls(int level, const char *string) {
47 fprintf(stderr, " => %s", string);
51 int main (int argc, char *argv[]) {
54 char buffer[MAX_REQUEST_LINE];
56 FILE *fd_read, *fd_write;
58 gnutls_session_t session;
59 gnutls_certificate_credentials_t xcred;
61 gnutls_x509_crt_t cert;
62 const gnutls_datum_t *cert_list;
63 unsigned int cert_list_size;
65 if (argc != 5 && argc != 6) {
67 "Usage: %s <ca-file> <hostname> <port> <hostname-verify> "
68 "[<digest-authentication>]\n"
70 "tlsproxy must be running on port 4711, "
71 "<port> is the <hostname> port.\n",
77 gnutls_certificate_allocate_credentials(&xcred);
80 gnutls_global_set_log_level(10);
81 gnutls_global_set_log_function(log_function_gnutls);
84 gnutls_certificate_set_x509_trust_file(xcred,
85 argv[1], GNUTLS_X509_FMT_PEM);
87 gnutls_init(&session, GNUTLS_CLIENT);
88 gnutls_priority_set_direct(session, "NORMAL", NULL);
89 gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
91 server = connect_to_host("localhost", "4711");
95 if (fdopen_read_write(server, &fd_read, &fd_write) != 0) {
99 /* Talk to tlsproxy. */
100 fprintf(fd_write, "CONNECT %s:%s HTTP/1.0\r\n", argv[2], argv[3]);
102 fprintf(fd_write, "Proxy-Authorization: Basic %s\r\n", argv[5]);
104 fprintf(fd_write, "\r\n");
106 if (read_http_request(fd_read, buffer, sizeof(buffer)) != 0) {
107 fprintf(stderr, "invalid proxy response\n");
111 printf("response: %s\n", buffer);
113 if (sscanf(buffer, "HTTP/1.0 %d", &response) != 1) {
114 fprintf(stderr, "invalid proxy response: %s\n", buffer);
118 if (response != 200) {
119 fprintf(stderr, "proxy failure\n");
123 #ifdef HAVE_GNUTLS_TRANSPORT_SET_INT2
124 /* gnutls_transport_set_int() is a macro. */
125 gnutls_transport_set_int(session, server);
127 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)server);
130 result = gnutls_handshake(session);
131 if (result != GNUTLS_E_SUCCESS) {
132 fprintf(stderr, "gnutls_handshake() failed\n");
133 gnutls_perror(result);
137 /* Verify the proxy certificate. */
138 result = gnutls_certificate_verify_peers2(session, &status);
140 fprintf(stderr, "gnutls_certificate_verify_peers2() failed\n");
141 gnutls_perror(result);
145 if (status & GNUTLS_CERT_INVALID) {
146 fprintf(stderr, "certificate invalid\n");
149 /* Get proxy certificate. */
150 if ((result = gnutls_x509_crt_init(&cert)) < 0) {
151 fprintf(stderr, "gnutls_x509_crt_init() failed");
152 gnutls_perror(result);
156 cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
157 if (cert_list == NULL) {
158 fprintf(stderr, "gnutls_certificate_get_peers() failed");
162 if ((result = gnutls_x509_crt_import(cert, &cert_list[0],
163 GNUTLS_X509_FMT_DER)) < 0) {
164 fprintf(stderr, "gnutls_x509_crt_import() failed");
165 gnutls_perror(result);
169 /* Check hostname. */
170 if (!gnutls_x509_crt_check_hostname(cert, argv[4])) {
171 fprintf(stderr, "hostname didn't match '%s'\n", argv[4]);
175 gnutls_x509_crt_deinit(cert);
177 /* Send a bogus request to the server. Otherwise recent gnutls-serv won't
178 * terminate the connection when gnutls_bye() is used. */
179 gnutls_record_send(session, "GET / HTTP/1.0\r\n\r\n",
180 strlen("GET / HTTP/1.0\r\n\r\n"));
182 gnutls_bye(session, GNUTLS_SHUT_RDWR);
186 gnutls_deinit(session);
187 gnutls_certificate_free_credentials(xcred);
188 gnutls_global_deinit();
194 /* Copied from src/connection.c (and removed LOG_* stuff)! Don't modify. */
196 static int fdopen_read_write(int socket, FILE **read_fd, FILE **write_fd) {
197 *read_fd = fdopen(socket, "r");
198 if (*read_fd == NULL) {
199 perror("fdopen_read_write(): fdopen(\"r\") failed");
203 *write_fd = fdopen(dup(socket), "w");
204 if (*write_fd == NULL) {
205 perror("fdopen_read_write(): fdopen(\"w\") failed");
207 *read_fd = NULL; /* "tell" caller read_fd is already closed */
214 static int connect_to_host(const char *hostname, const char *port) {
215 struct addrinfo gai_hints;
216 struct addrinfo *gai_result;
220 struct addrinfo *server;
222 if (hostname == NULL || port == NULL) {
226 /* Get IP of hostname server. */
227 memset(&gai_hints, 0, sizeof(gai_hints));
228 gai_hints.ai_family = AF_UNSPEC;
229 gai_hints.ai_socktype = SOCK_STREAM;
230 gai_hints.ai_protocol = 0;
231 gai_hints.ai_flags = AI_NUMERICSERV /* given port is numeric */
233 | AI_ADDRCONFIG /* supported by this computer */
236 gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
237 if (gai_return != 0) {
238 if (gai_return == EAI_SYSTEM) {
239 perror("connect_to_host(): getaddrinfo()");
241 fprintf(stderr, "connect_to_host(): getaddrinfo(): %s",
242 gai_strerror(gai_return));
247 /* Now try to connect to each server returned by getaddrinfo(), use the
248 * first successful connect. */
249 for (server = gai_result; server != NULL; server = server->ai_next) {
250 server_socket = socket(server->ai_family,
252 server->ai_protocol);
253 if (server_socket < 0) {
254 perror("connect_to_host(): socket(), trying next");
258 if (connect(server_socket, server->ai_addr, server->ai_addrlen) == 0) {
261 perror("connect_to_host(): connect(), trying next");
263 close(server_socket);
265 /* Make sure we free the result from getaddrinfo(). */
266 freeaddrinfo(gai_result);
268 if (server == NULL) {
269 perror("connect_to_host(): no server found, abort");
273 return server_socket;
276 static int read_http_request(FILE *client_fd, char *request, size_t length) {
277 char buffer[MAX_REQUEST_LINE];
279 assert(length <= INT_MAX);
280 if (fgets(request, (int)length, client_fd) == NULL) {
281 if (ferror(client_fd)) {
282 perror("read_http_request(): fgets()");
289 while (fgets(buffer, sizeof(buffer), client_fd) != NULL) {
291 if (!strcmp(buffer, "\n") || !strcmp(buffer, "\r\n")) {
295 if (ferror(client_fd)) {
296 perror("read_http_request(): fgets()");
298 } else if (feof(client_fd)) {