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