]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/blob - tests/client.c
9bb23f392ab2831a896dc859d5fc76b9d05c6acc
[tlsproxy/tlsproxy.git] / tests / client.c
1 /*
2  * Simple GnuTLS client used for testing.
3  *
4  * Copyright (C) 2011-2013  Simon Ruderich
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include <config.h>
21
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include <gnutls/gnutls.h>
33 #include <gnutls/x509.h>
34
35
36 #define MAX_REQUEST_LINE 4096
37
38 static int connect_to_host(const char *hostname, const char *port);
39 static int read_http_request(FILE *client_fd, char *request, size_t length);
40
41
42 int main (int argc, char *argv[]) {
43     int result, response;
44     unsigned int status;
45     char buffer[MAX_REQUEST_LINE];
46     int server;
47     FILE *fd;
48
49     gnutls_session_t session;
50     gnutls_certificate_credentials_t xcred;
51
52     gnutls_x509_crt_t cert;
53     const gnutls_datum_t *cert_list;
54     unsigned int cert_list_size;
55
56     if (argc != 5) {
57         fprintf(stderr,
58                 "Usage: %s <ca-file> <hostname> <port> <hostname-verify>\n",
59                 argv[0]);
60         return EXIT_FAILURE;
61     }
62
63     gnutls_global_init();
64     gnutls_certificate_allocate_credentials(&xcred);
65
66     gnutls_certificate_set_x509_trust_file(xcred,
67                                            argv[1], GNUTLS_X509_FMT_PEM);
68
69     gnutls_init(&session, GNUTLS_CLIENT);
70     gnutls_priority_set_direct(session, "NORMAL", NULL);
71     gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
72
73     server = connect_to_host("localhost", "4711");
74     if (server == -1) {
75         return EXIT_FAILURE;
76     }
77     fd = fdopen(server, "a+");
78     if (fd == NULL) {
79         perror("fdopen()");
80         return EXIT_FAILURE;
81     }
82
83     /* Talk to tlsproxy. */
84     fprintf(fd, "CONNECT %s:%s HTTP/1.0\r\n", argv[2], argv[3]);
85     fprintf(fd, "\r\n");
86     fflush(fd);
87     if (read_http_request(fd, buffer, sizeof(buffer)) == -1) {
88         fprintf(stderr, "invalid proxy response\n");
89         return EXIT_FAILURE;
90     }
91
92     printf("response: %s\n", buffer);
93
94     if (sscanf(buffer, "HTTP/1.0 %d", &response) != 1) {
95         fprintf(stderr, "invalid proxy response: %s\n", buffer);
96         return EXIT_FAILURE;
97     }
98
99     if (response != 200) {
100         fprintf(stderr, "proxy failure\n");
101         return EXIT_FAILURE;
102     }
103
104     gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)server);
105
106     result = gnutls_handshake(session);
107     if (result != GNUTLS_E_SUCCESS) {
108         fprintf(stderr, "gnutls_handshake() failed\n");
109         gnutls_perror(result);
110         return EXIT_FAILURE;
111     }
112
113     /* Verify the proxy certificate. */
114     result = gnutls_certificate_verify_peers2(session, &status);
115     if (result < 0) {
116         fprintf(stderr, "gnutls_certificate_verify_peers2() failed\n");
117         gnutls_perror(result);
118         return EXIT_FAILURE;
119     }
120
121     if (status & GNUTLS_CERT_INVALID) {
122         fprintf(stderr, "certificate invalid\n");
123     }
124
125     /* Get proxy certificate. */
126     if ((result = gnutls_x509_crt_init(&cert)) < 0) {
127         fprintf(stderr, "gnutls_x509_crt_init() failed");
128         gnutls_perror(result);
129         return EXIT_FAILURE;
130     }
131
132     cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
133     if (cert_list == NULL) {
134         fprintf(stderr, "gnutls_certificate_get_peers() failed");
135         return EXIT_FAILURE;
136     }
137
138     if ((result = gnutls_x509_crt_import(cert, &cert_list[0],
139                                          GNUTLS_X509_FMT_DER)) < 0) {
140         fprintf(stderr, "gnutls_x509_crt_import() failed");
141         gnutls_perror(result);
142         return EXIT_FAILURE;
143     }
144
145     /* Check hostname. */
146     if (!gnutls_x509_crt_check_hostname(cert, argv[4])) {
147         fprintf(stderr, "hostname didn't match '%s'\n", argv[4]);
148         return EXIT_FAILURE;
149     }
150
151     gnutls_x509_crt_deinit(cert);
152
153     gnutls_bye(session, GNUTLS_SHUT_RDWR);
154     fclose(fd);
155
156     gnutls_deinit(session);
157     gnutls_certificate_free_credentials(xcred);
158     gnutls_global_deinit();
159
160     return EXIT_SUCCESS;
161 }
162
163
164 /* Copied from src/connection.c (and removed LOG_* stuff)! Don't modify. */
165
166 static int connect_to_host(const char *hostname, const char *port) {
167     struct addrinfo gai_hints;
168     struct addrinfo *gai_result;
169     int gai_return;
170
171     int server_socket;
172     struct addrinfo *server;
173
174     if (hostname == NULL || port == NULL) {
175         return -1;
176     }
177
178     /* Get IP of hostname server. */
179     memset(&gai_hints, 0, sizeof(gai_hints));
180     gai_hints.ai_family   = AF_UNSPEC;
181     gai_hints.ai_socktype = SOCK_STREAM;
182     gai_hints.ai_protocol = 0;
183     gai_hints.ai_flags    = AI_NUMERICSERV /* given port is numeric */
184                           | AI_ADDRCONFIG  /* supported by this computer */
185                           | AI_V4MAPPED;   /* support IPv4 through IPv6 */
186     gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
187     if (gai_return != 0) {
188         if (gai_return == EAI_SYSTEM) {
189             perror("connect_to_host(): getaddrinfo()");
190         } else {
191             fprintf(stderr, "connect_to_host(): getaddrinfo(): %s",
192                             gai_strerror(gai_return));
193         }
194         return -1;
195     }
196
197     /* Now try to connect to each server returned by getaddrinfo(), use the
198      * first successful connect. */
199     for (server = gai_result; server != NULL; server = server->ai_next) {
200         server_socket = socket(server->ai_family,
201                                server->ai_socktype,
202                                server->ai_protocol);
203         if (server_socket == -1) {
204             perror("connect_to_host(): socket(), trying next");
205             continue;
206         }
207
208         if (connect(server_socket, server->ai_addr, server->ai_addrlen) != -1) {
209             break;
210         }
211         perror("connect_to_host(): connect(), trying next");
212
213         close(server_socket);
214     }
215     /* Make sure we free the result from getaddrinfo(). */
216     freeaddrinfo(gai_result);
217
218     if (server == NULL) {
219         perror("connect_to_host(): no server found, abort");
220         return -1;
221     }
222
223     return server_socket;
224 }
225
226 static int read_http_request(FILE *client_fd, char *request, size_t length) {
227     char buffer[MAX_REQUEST_LINE];
228
229     if (fgets(request, (int)length, client_fd) == NULL) {
230         if (ferror(client_fd)) {
231             perror("read_http_request(): fgets()");
232             return -1;
233         }
234         /* EOF */
235         return -2;
236     }
237
238     while (fgets(buffer, MAX_REQUEST_LINE, client_fd) != NULL) {
239         /* End of header. */
240         if (!strcmp(buffer, "\n") || !strcmp(buffer, "\r\n")) {
241             break;
242         }
243     }
244     if (ferror(client_fd)) {
245         perror("read_http_request(): fgets()");
246         return -1;
247     }
248
249     return 0;
250 }