]> ruderich.org/simon Gitweb - tlsproxy/tlsproxy.git/blob - tests/client.c
tests: Add. Test basic behavior.
[tlsproxy/tlsproxy.git] / tests / client.c
1 /*
2  * Simple GnuTLS client used for testing.
3  *
4  * Copyright (C) 2011  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     }
155
156     gnutls_x509_crt_deinit(cert);
157
158     gnutls_bye(session, GNUTLS_SHUT_RDWR);
159     fclose(fd);
160
161     gnutls_deinit(session);
162     gnutls_certificate_free_credentials(xcred);
163     gnutls_global_deinit();
164
165     return EXIT_SUCCESS;
166 }
167
168
169 /* Copied from src/connection.c (and removed LOG_* stuff)! Don't modify. */
170
171 static int connect_to_host(const char *hostname, const char *port) {
172     struct addrinfo gai_hints;
173     struct addrinfo *gai_result;
174     int gai_return;
175
176     int server_socket;
177     struct addrinfo *server;
178
179     if (NULL == hostname || NULL == port) {
180         return -1;
181     }
182
183     /* Get IP of hostname server. */
184     memset(&gai_hints, 0, sizeof(gai_hints));
185     gai_hints.ai_family   = AF_UNSPEC;
186     gai_hints.ai_socktype = SOCK_STREAM;
187     gai_hints.ai_protocol = 0;
188     gai_hints.ai_flags    = AI_NUMERICSERV /* given port is numeric */
189                           | AI_ADDRCONFIG  /* supported by this computer */
190                           | AI_V4MAPPED;   /* support IPv4 through IPv6 */
191     gai_return = getaddrinfo(hostname, port, &gai_hints, &gai_result);
192     if (0 != gai_return) {
193         perror("connect_to_host(): getaddrinfo()");
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; NULL != server; server = server->ai_next) {
200         server_socket = socket(server->ai_family,
201                                server->ai_socktype,
202                                server->ai_protocol);
203         if (-1 == server_socket) {
204             perror("connect_to_host(): socket(), trying next");
205             continue;
206         }
207
208         if (-1 != connect(server_socket, server->ai_addr,
209                                          server->ai_addrlen)) {
210             break;
211         }
212         perror("connect_to_host(): connect(), trying next");
213
214         close(server_socket);
215     }
216     /* Make sure we free the result from getaddrinfo(). */
217     freeaddrinfo(gai_result);
218
219     if (NULL == server) {
220         perror("connect_to_host(): no server found, abort");
221         return -1;
222     }
223
224     return server_socket;
225 }
226
227 static int read_http_request(FILE *client_fd, char *request, size_t length) {
228     char buffer[MAX_REQUEST_LINE];
229
230     if (NULL == fgets(request, (int)length, client_fd)) {
231         if (ferror(client_fd)) {
232             perror("read_http_request(): fgets()");
233             return -1;
234         }
235
236         return -2;
237     }
238
239     while (NULL != fgets(buffer, MAX_REQUEST_LINE, client_fd)) {
240         /* End of header. */
241         if (0 == strcmp(buffer, "\n") || 0 == strcmp(buffer, "\r\n")) {
242             break;
243         }
244     }
245     if (ferror(client_fd)) {
246         perror("read_http_request(): fgets()");
247         return -1;
248     }
249
250     return 0;
251 }