summaryrefslogtreecommitdiff
path: root/src/examples/ecore/ecore_fd_handler_gnutls_example.c
blob: 6cd5ba1eb10866c01c473c6b825747d20cb91be3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//Compile with:
// gcc -o ecore_fd_handler_gnutls_example ecore_fd_handler_gnutls_example.c `pkg-config --cflags --libs ecore gnutls`

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <fcntl.h>
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include <errno.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#include <Ecore.h>

/* Ecore_Fd_Handler example
 * 2010 Mike Blumenkrantz
 * compile with gcc $(pkgconfig --cflags --libs gnutls ecore)
 */

#define print(...) \
do { \
  fprintf(stderr, "line %i: ", __LINE__); \
  fprintf(stderr, __VA_ARGS__); \
  fprintf(stderr, "\n");\
} while(0)

static int done = 0;

static void
tls_log_func(int level, const char *str)
{
   fprintf(stderr, "|<%d>| %s", level, str);
}

static const char *
SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_description_t status)
{
   switch (status)
     {
      case GNUTLS_HANDSHAKE_HELLO_REQUEST:
        return "Hello request";

      case GNUTLS_HANDSHAKE_CLIENT_HELLO:
        return "Client hello";

      case GNUTLS_HANDSHAKE_SERVER_HELLO:
        return "Server hello";

      case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:
        return "Certificate packet";

      case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE:
        return "Server key exchange";

      case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
        return "Certificate request";

      case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE:
        return "Server hello done";

      case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
        return "Certificate verify";

      case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
        return "Client key exchange";

      case GNUTLS_HANDSHAKE_FINISHED:
        return "Finished";

      case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
        return "Supplemental";
      default:
        return "Uncaught state";
     }
   return NULL;
}

/* Connects to the peer and returns a socket
 * descriptor.
 */
static int
tcp_connect(void)
{
   const char *PORT = "443";
   const char *SERVER = "69.58.181.89"; //verisign.com
   int err, sd;
   int flag = 1, curstate = 0;
   struct sockaddr_in sa;

   /* sets some fd options such as nonblock */
   sd = socket(AF_INET, SOCK_STREAM, 0);
   fcntl(sd, F_SETFL, O_NONBLOCK);
   eina_file_close_on_exec(sd, EINA_TRUE);
   setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, sizeof(curstate));

   setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));

   memset(&sa, '\0', sizeof (sa));
   sa.sin_family = AF_INET;
   sa.sin_port = htons(atoi(PORT));
   inet_pton(AF_INET, SERVER, &sa.sin_addr);

   /* connects to server
    */
   err = connect(sd, (struct sockaddr *)&sa, sizeof (sa));
   if ((err < 0) && (errno != EINPROGRESS))
     {
        print("Connect error\n");
        exit(1);
     }

   return sd;
}

/* closes the given socket descriptor.
 */
static void
tcp_close(int sd)
{
#ifdef _WIN32
   shutdown(sd, SD_BOTH);     /* no more receptions */
   closesocket(sd);
#else
   shutdown(sd, SHUT_RDWR);     /* no more receptions */
   close(sd);
#endif
}

static Eina_Bool
_process_data(gnutls_session_t client, Ecore_Fd_Handler *fd_handler)
{
   static int ret, lastret;
   static unsigned int count = 0;

   if (!done)
     {
        lastret = ret;
        ret = gnutls_handshake(client);
        count++;
        if (gnutls_record_get_direction(client))
          ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_WRITE);
        else
          ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_READ);
        /* avoid printing messages infinity times */
        if (lastret != ret)
          {
             print("gnutls returned with: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret));
             if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED))
               print("Also received alert: %s", gnutls_alert_get_name(gnutls_alert_get(client)));
             print("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_out(client)));
             print("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_in(client)));
          }

        if (gnutls_error_is_fatal(ret))
          {
             print("yarrr this be an error!");
             exit(1);
          }
     }
   if (ret == GNUTLS_E_SUCCESS)
     {
        done = 1;
        print("Handshake successful in %u handshake calls!", count);
        ecore_main_loop_quit();
     }

   return ECORE_CALLBACK_RENEW;
}

int
main(void)
{
   /* credentials */
   gnutls_anon_client_credentials_t c_anoncred;
   gnutls_certificate_credentials_t c_certcred;

   gnutls_session_t client;
   int sd;

   /* General init. */
   gnutls_global_init();
   ecore_init();
   gnutls_global_set_log_function(tls_log_func);
   gnutls_global_set_log_level(6);

   /* Init client */
   gnutls_anon_allocate_client_credentials(&c_anoncred);
   gnutls_certificate_allocate_credentials(&c_certcred);
   gnutls_init(&client, GNUTLS_CLIENT);
   /* set very specific priorities */
   gnutls_priority_set_direct(client, "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+VERS-SSL3.0", NULL);
   gnutls_credentials_set(client, GNUTLS_CRD_ANON, c_anoncred);
   gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, c_certcred);
   gnutls_server_name_set(client, GNUTLS_NAME_DNS, "www.verisign.com", strlen("www.verisign.com"));

   /* connect to the peer
    */
   sd = tcp_connect();

   /* associate gnutls with socket */
   gnutls_transport_set_ptr(client, (gnutls_transport_ptr_t)(uintptr_t)sd);
   /* add a callback for data being available for send/receive on socket */
   if (!ecore_main_fd_handler_add(sd, ECORE_FD_READ | ECORE_FD_WRITE, (Ecore_Fd_Cb)_process_data, client, NULL, NULL))
     {
        print("could not create fd handler!");
        exit(1);
     }
   /* begin main loop */
   ecore_main_loop_begin();

   gnutls_bye(client, GNUTLS_SHUT_RDWR);

   gnutls_deinit(client);

   tcp_close(sd);

   return 0;
}