/* * 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; }