1//
2//  main.c
3//  tlsnketest
4//
5//  Created by Fabrice Gautier on 12/7/11.
6//  Copyright (c) 2011 Apple, Inc. All rights reserved.
7//
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12
13#include <unistd.h>
14#include <sys/socket.h>
15#include <netinet/in.h>
16#include <arpa/inet.h>
17#include <net/kext_net.h>
18#include <pthread.h>
19#include <netdb.h>
20#include <fcntl.h>
21
22#include <stdbool.h>
23
24#include <AssertMacros.h>
25#include "tlssocket.h"
26#include "tlsnke.h"
27
28
29static void print_data(const char *s, size_t l, const unsigned char *p)
30{
31    printf("%s, %zu:",s, l);
32    for(int i=0; i<l; i++)
33        printf(" %02x", p[i]);
34    printf("\n");
35}
36
37static void *server_thread_func(void *arg)
38{
39    int sock;
40    struct sockaddr_in server_addr;
41    int err;
42
43    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
44        perror("server socket");
45        exit(1);
46    }
47
48    // Dont use TLSSocket_Attach for the server:
49    // TLSSocket_Attach can only open one TLS socket at a time.
50    {
51        struct so_nke so_tlsnke;
52
53        memset(&so_tlsnke, 0, sizeof(so_tlsnke));
54        so_tlsnke.nke_handle = TLS_HANDLE_IP4;
55        err=setsockopt(sock, SOL_SOCKET, SO_NKE, &so_tlsnke, sizeof(so_tlsnke));
56        if(err<0) {
57            perror("attach (server)");
58            exit(err);
59        }
60    }
61
62    server_addr.sin_family = AF_INET;
63    server_addr.sin_port = htons(23232);
64    server_addr.sin_addr.s_addr = INADDR_ANY;
65    bzero(&(server_addr.sin_zero),8);
66
67    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))
68        == -1) {
69        perror("Unable to bind");
70        exit(1);
71    }
72
73    printf("\nBound - Server Waiting for client on port 23232\n");
74    fflush(stdout);
75
76    while (1)
77    {
78        int rc;
79        SSLRecord rec;
80        rc=TLSSocket_Funcs.read((intptr_t)sock, &rec);
81        if(!rc) {
82            print_data("recvd", rec.contents.length, rec.contents.data);
83            rec.contents.data[rec.contents.length-1]=0;
84            printf("recvd: %ld, %s\n", rec.contents.length, rec.contents.data);
85            free(rec.contents.data);
86        } else {
87            printf("read failed: %d\n", rc);
88        }
89    }
90
91    close(sock);
92    return NULL;
93}
94
95static int create_client_socket(const char *hostname)
96{
97    int sock;
98    int err;
99
100
101    printf("Create client socket\n");
102    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
103    if(sock<0) {
104        perror("client socket");
105        return sock;
106    }
107
108
109#if 1
110    err=TLSSocket_Attach(sock);
111    if(err<0) {
112        perror("TLSSocket_Attach (server)");
113        exit(err);
114    }
115#endif
116
117
118    struct hostent *host;
119    struct sockaddr_in server_addr;
120
121    //host = gethostbyname("kruk.apple.com");
122    //host = gethostbyname("localhost");
123    host= gethostbyname(hostname);
124    if(!host) {
125        herror("host");
126        return -1;
127    }
128    server_addr.sin_family = AF_INET;
129    server_addr.sin_port = htons(23232);
130    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
131    bzero(&(server_addr.sin_zero),8);
132
133    err = connect(sock, (struct sockaddr *)&server_addr,
134                  sizeof(struct sockaddr));
135    if(err)
136    {
137        perror("connect");
138        return err;
139    }
140
141    return sock;
142}
143
144/* simple test */
145static int kext_test(const char *hostname, int bypass)
146{
147    int sock, i;
148    char send_data[1024];
149    int tlsfd;
150    pthread_t server_thread;
151
152    if(strcmp(hostname, "localhost")==0) {
153        pthread_create(&server_thread, NULL, server_thread_func, NULL);
154        // Just wait for the server to be setup
155        sleep(1);
156    }
157
158
159    sock = create_client_socket(hostname);
160
161    if(bypass) {
162        /* Have to open this after we attached the filter to the client socket */
163        tlsfd=open("/dev/tlsnke", O_RDWR);
164        if(tlsfd<0) {
165            perror("open tlsnke");
166            exit(1);
167        }
168    }
169
170
171    for(i=0; i<20;i++) {
172        int n;
173        ssize_t err;
174        n=sprintf(send_data, "Message #%d\n", i);
175        if(n<0) {
176            perror("sprintf");
177            exit(1);
178        }
179
180        printf("Client(1) sending %d bytes (\"%s\")\n", n, send_data);
181
182        if(bypass) {
183            err = write(tlsfd, send_data, n);
184            if(err<0) {
185                perror("write to tlsnke");
186                exit(1);
187            }
188        } else {
189            SSLRecord rec;
190
191            rec.contentType = SSL_RecordTypeAppData;
192            rec.protocolVersion = DTLS_Version_1_0;
193            rec.contents.data = (uint8_t *)send_data;
194            rec.contents.length = n;
195
196            err = TLSSocket_Funcs.write((intptr_t)sock, rec);
197            if(err<0) {
198                perror("write to socket");
199                exit(1);
200            }
201
202            /* serviceWriteQueue every 2 writes, this will trigger rdar://11348395 */
203            if(i&1) {
204                int err;
205                err = TLSSocket_Funcs.serviceWriteQueue((intptr_t)sock);
206                if(err<0) {
207                    perror("service write queue");
208                    exit(1);
209                }
210            }
211        }
212
213        sleep(1);
214    }
215
216    return 0;
217}
218
219
220/* handshake test */
221int st_test();
222
223/* echo test */
224int dtls_client(const char *hostname, int bypass);
225
226static
227int usage(const char *argv0)
228{
229    printf("Usage: %s <test> <hostname> <bypass>\n", argv0);
230    printf("     <test>: type of test: 's'imple, 'h'andshake or 'e'cho] (see below)\n");
231    printf("     <hostname>: hostname of server\n");
232    printf("     <bypass>: use /dev/tlsnke bypass test\n");
233
234    printf("\n    'S'imple test:\n"
235           "\tVery basic test with no handshake. DTLS packets are sent through the socket filter, non encrypted.\n"
236           "\tIf hostname is 'localhost', a local simple server will be created that will also use the tls filter,\n"
237           "\tsuch that the input path is tested.\n"
238           "\tOtherwise, a server on the other side is not required only the output path is tested. If there is no server replying\n"
239           "\tonly the ouput path will be tested. If a server is replying, input packet will be processed but are never read to userspace\n"
240           "\tif bypass=1, also send the same packet through the /dev/tlsnke interface, as if they were coming from utun\n");
241
242    printf("\n    'H'andshake:\n");
243    printf("\tTest SSL Handshake with various ciphers, between a local client going through the tlsnke\n"
244           "\tfilter, and a local server using only the userland SecureTransport.\n"
245           "\thostname and bypass are ignored.\n");
246
247    printf("\n    'E'cho:\n");
248    printf("\tTest to connect to an udp echo server indicated by hostname, on port 23232.\n"
249           "\tSet bypass=1 to use the /dev/tlsnke bsd device to send/recv the app data (emulate utun behaviour)\n");
250
251    printf("\n\tbypass=1 require the tlsnke kext to be compiled with TLS_TEST=1 (not the default in the build)\n");
252
253    return -1;
254}
255
256int main (int argc, const char * argv[])
257{
258
259    printf("argv0=%s argc=%d\n", argv[0], argc);
260    if(argc<2)
261        return usage(argv[0]);
262
263    switch (argv[1][0]) {
264    case 's':
265    case 'S':
266            if(argc<3) return usage(argv[0]);
267            return kext_test(argv[2], atoi(argv[3])?1:0);
268    case 'h':
269    case 'H':
270            return st_test();
271    case 'e':
272    case 'E':
273            if(argc<3) return usage(argv[0]);
274            return dtls_client(argv[2], atoi(argv[3])?1:0);
275    default:
276            return usage(argv[0]);
277    }
278}
279
280