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