/*
* Simple GnuTLS client used for testing.
*
* Copyright (C) 2011-2013 Simon Ruderich
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_REQUEST_LINE 4096
static int connect_to_host(const char *hostname, const char *port);
static int read_http_request(FILE *client_fd, char *request, size_t length);
int main (int argc, char *argv[]) {
int result, response;
unsigned int status;
char buffer[MAX_REQUEST_LINE];
int server;
FILE *fd;
gnutls_session_t session;
gnutls_certificate_credentials_t xcred;
gnutls_x509_crt_t cert;
const gnutls_datum_t *cert_list;
unsigned int cert_list_size;
if (5 != argc) {
fprintf(stderr,
"Usage: %s \n",
argv[0]);
return EXIT_FAILURE;
}
gnutls_global_init();
gnutls_certificate_allocate_credentials(&xcred);
gnutls_certificate_set_x509_trust_file(xcred,
argv[1], GNUTLS_X509_FMT_PEM);
gnutls_init(&session, GNUTLS_CLIENT);
gnutls_priority_set_direct(session, "NORMAL", NULL);
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
server = connect_to_host("localhost", "4711");
if (-1 == server) {
return EXIT_FAILURE;
}
fd = fdopen(server, "a+");
if (NULL == fd) {
perror("fdopen()");
return EXIT_FAILURE;
}
/* Talk to tlsproxy. */
fprintf(fd, "CONNECT %s:%s HTTP/1.0\r\n", argv[2], argv[3]);
fprintf(fd, "\r\n");
fflush(fd);
if (-1 == read_http_request(fd, buffer, sizeof(buffer))) {
fprintf(stderr, "invalid proxy response\n");
return EXIT_FAILURE;
}
printf("response: %s\n", buffer);
if (1 != sscanf(buffer, "HTTP/1.0 %d", &response)) {
fprintf(stderr, "invalid proxy response: %s\n", buffer);
return EXIT_FAILURE;
}
if (200 != response) {
fprintf(stderr, "proxy failure\n");
return EXIT_FAILURE;
}
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)server);
result = gnutls_handshake(session);
if (GNUTLS_E_SUCCESS != result) {
fprintf(stderr, "gnutls_handshake() failed\n");
gnutls_perror(result);
return EXIT_FAILURE;
}
/* Verify the proxy certificate. */
result = gnutls_certificate_verify_peers2(session, &status);
if (0 > result) {
fprintf(stderr, "gnutls_certificate_verify_peers2() failed\n");
gnutls_perror(result);
return EXIT_FAILURE;
}
if (status & GNUTLS_CERT_INVALID) {
fprintf(stderr, "certificate invalid\n");
}
/* Get proxy certificate. */
if (0 > (result = gnutls_x509_crt_init(&cert))) {
fprintf(stderr, "gnutls_x509_crt_init() failed");
gnutls_perror(result);
return EXIT_FAILURE;
}
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
if (NULL == cert_list) {
fprintf(stderr, "gnutls_certificate_get_peers() failed");
return EXIT_FAILURE;
}
if (0 > (result = gnutls_x509_crt_import(cert, &cert_list[0],
GNUTLS_X509_FMT_DER))) {
fprintf(stderr, "gnutls_x509_crt_import() failed");
gnutls_perror(result);
return EXIT_FAILURE;
}
/* Check hostname. */
if (!gnutls_x509_crt_check_hostname(cert, argv[4])) {
fprintf(stderr, "hostname didn't match '%s'\n", argv[4]);
return EXIT_FAILURE;
}
gnutls_x509_crt_deinit(cert);
gnutls_bye(session, GNUTLS_SHUT_RDWR);
fclose(fd);
gnutls_deinit(session);
gnutls_certificate_free_credentials(xcred);
gnutls_global_deinit();
return EXIT_SUCCESS;
}
/* Copied from src/connection.c (and removed LOG_* stuff)! Don't modify. */
static int connect_to_host(const char *hostname, const char *port) {
struct addrinfo gai_hints;
struct addrinfo *gai_result;
int gai_return;
int server_socket;
struct addrinfo *server;
if (NULL == hostname || NULL == port) {
return -1;
}
/* Get IP of hostname server. */
memset(&gai_hints, 0, sizeof(gai_hints));
gai_hints.ai_family = AF_UNSPEC;
gai_hints.ai_socktype = SOCK_STREAM;
gai_hints.ai_protocol = 0;
gai_hints.ai_flags = AI_NUMERICSERV /* given port is numeric */
| AI_ADDRCONFIG /* supported by this computer */
| AI_V4MAPPED; /* support IPv4 through IPv6 */
gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
if (0 != gai_return) {
perror("connect_to_host(): getaddrinfo()");
return -1;
}
/* Now try to connect to each server returned by getaddrinfo(), use the
* first successful connect. */
for (server = gai_result; NULL != server; server = server->ai_next) {
server_socket = socket(server->ai_family,
server->ai_socktype,
server->ai_protocol);
if (-1 == server_socket) {
perror("connect_to_host(): socket(), trying next");
continue;
}
if (-1 != connect(server_socket, server->ai_addr,
server->ai_addrlen)) {
break;
}
perror("connect_to_host(): connect(), trying next");
close(server_socket);
}
/* Make sure we free the result from getaddrinfo(). */
freeaddrinfo(gai_result);
if (NULL == server) {
perror("connect_to_host(): no server found, abort");
return -1;
}
return server_socket;
}
static int read_http_request(FILE *client_fd, char *request, size_t length) {
char buffer[MAX_REQUEST_LINE];
if (NULL == fgets(request, (int)length, client_fd)) {
if (ferror(client_fd)) {
perror("read_http_request(): fgets()");
return -1;
}
return -2;
}
while (NULL != fgets(buffer, MAX_REQUEST_LINE, client_fd)) {
/* End of header. */
if (0 == strcmp(buffer, "\n") || 0 == strcmp(buffer, "\r\n")) {
break;
}
}
if (ferror(client_fd)) {
perror("read_http_request(): fgets()");
return -1;
}
return 0;
}