4.2.1 that we had on our wiki
[tclink.git] / tclink.c
CommitLineData
d667da5b
IK
1/* tclink.c - Library code for the TCLink client API.
2 *
3 * TCLink Copyright (c) 2013 TrustCommerce.
4 * http://www.trustcommerce.com
5 * techsupport@trustcommerce.com
6 * (949) 387-3747
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include "tclink.h"
24
25#include <stdio.h>
26#include <memory.h>
27#include <errno.h>
28#include <string.h>
29
30#ifdef WIN32
31#include <io.h>
32#include <winsock2.h>
33#else
34#include <strings.h>
35#include <sys/time.h>
36#include <sys/types.h>
37#include <sys/socket.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40#include <netdb.h>
41#include <unistd.h>
42#endif
43
44#include <sys/stat.h>
45#include <sys/types.h>
46#include <stdlib.h>
47#include <fcntl.h>
48#include <signal.h>
49
50#define OPENSSL_NO_KRB5 1
51
52#include <openssl/crypto.h>
53#include <openssl/x509.h>
54#include <openssl/pem.h>
55#include <openssl/ssl.h>
56#include <openssl/err.h>
57#include <openssl/rand.h>
58
59#ifdef WIN32
60#define strcasecmp(x,y) stricmp(x,y)
61#else
62#define closesocket(x) close(x)
63#endif
64
65#define DEFAULT_HOST "pgw1.trustcommerce.com"
66
67/* changed from forty second to one hundred second to reflect more complicated transaction processing logic */
68#define TIMEOUT 100 /* seconds */
69#define TC_BUFF_MAX 16000
70#define TC_LINE_MAX ((PARAM_MAX_LEN * 2) + 2)
71
72char *tclink_version = TCLINK_VERSION; /* TCLINK_VERSION is defined in Makefile */
73char *tclink_host = DEFAULT_HOST;
74int tclink_port = 443;
75
76/*************************************************/
77/* Data structures used only within this module. */
78/*************************************************/
79
80/* Variables used for transaction data. */
81
82typedef struct param_data
83{
84 char *name;
85 char *value;
86 struct param_data *next;
87} param;
88
89typedef struct _TCLinkCon
90{
91 /* Connection data */
92 int *ip;
93 int num_ips;
94 int sd;
95
96 /* SSL encryption */
97 const SSL_METHOD *meth;
98 long ctx_options;
99 SSL_CTX *ctx;
100 SSL *ssl;
101
102 /* Transaction parameters, sent and received */
103 param *send_param_list, *send_param_tail;
104 param *recv_param_list;
105
106 /* Connection status */
107 int is_error;
108 int pass;
109 time_t start_time;
110 int dns;
111
112 char * trusted_ca_pem;
113 int (*validate_cert)(int, void *);
114 int full_ssl_close;
115
116} TCLinkCon;
117
118/*************************************
119 * Internal functions, not exported. *
120 *************************************/
121
122/* Random number from min to max. */
123static int number(int min, int max)
124{
125 return (rand() % (max - min + 1)) + min;
126}
127
128/* Check if path points to a regular file */
129int is_regular_file(const char* path)
130{
131 struct stat st;
132 stat(path, &st);
133 return S_ISREG(st.st_mode);
134}
135
136/* Safe string copy and append functions. */
137#define SAFE_COPY(d, s) safe_copy((d), (s), sizeof(d));
138#define SAFE_APPEND(d, s) safe_append((d), (s), sizeof(d));
139
140static void safe_copy(char *dst, const char *src, int size)
141{
142 int len = strlen(src);
143 if (len < size)
144 strcpy(dst, src);
145 else {
146 strncpy(dst, src, size - 1);
147 dst[size-1] = 0;
148 }
149}
150
151static void safe_append(char *dst, const char *src, int size)
152{
153 int dlen = strlen(dst);
154 int slen = strlen(src);
155 int avail = size - dlen;
156 if (avail < 1)
157 return;
158
159 if (slen < avail)
160 strcpy(dst+dlen, src);
161 else {
162 strncpy(dst+dlen, src, avail - 1);
163 dst[size-1] = 0;
164 }
165}
166
167/* Add a parameter-value pair to the recieved list. */
168static void AddRecvParam(TCLinkCon *c, const char *name, const char *value)
169{
170 param *p;
171
172 if (name[0] == 0 || value[0] == 0)
173 return;
174
175 p = (param *)malloc(sizeof(param));
176 p->name = strdup(name);
177 p->value = strdup(value);
178 p->next = c->recv_param_list;
179 c->recv_param_list = p;
180}
181
182/* Add a string to the received list. */
183static int AddRecvString(TCLinkCon *c, char *string)
184{
185 char *ptr = strchr(string, '=');
186 if (ptr == NULL)
187 return 0;
188
189 *ptr = 0;
190 AddRecvParam(c, string, ptr+1);
191
192 return 1;
193}
194
195/* Deallocate the send list. */
196static void ClearSendList(TCLinkCon *c)
197{
198 param *p, *next;
199 for (p = c->send_param_list; p; p = next)
200 {
201 next = p->next;
202 free(p->name);
203 free(p->value);
204 free(p);
205 }
206
207 c->send_param_list = c->send_param_tail = NULL;
208}
209
210/* Deallocate the recv list. */
211static void ClearRecvList(TCLinkCon *c)
212{
213 param *p, *next;
214 for (p = c->recv_param_list; p; p = next)
215 {
216 next = p->next;
217 free(p->name);
218 free(p->value);
219 free(p);
220 }
221
222 c->recv_param_list = NULL;
223}
224
225/* Open a socket to the host_ip specified. Returns the socket's file
226 * descriptor on success (the open attempt is underway) or -1 for failure
227 * (should never happen in practice). Note that this function DOES NOT block
228 * and wait for the connection; you'll need to select() on the socket later to see
229 * if it opened successfully.
230 */
231static int BeginConnection(TCLinkCon *c, int host_ip)
232{
233 struct sockaddr_in sa;
234 int sd;
235
236 sd = socket(AF_INET, SOCK_STREAM, 0);
237 if (sd < 0)
238 return -1;
239
240#ifdef WIN32
241 u_long param = 1;
242 ioctlsocket(sd, FIONBIO, &param);
243#else
244 fcntl(sd, F_SETFL, O_NONBLOCK);
245#endif
246
247 memset(&sa, 0, sizeof(sa));
248 sa.sin_family = AF_INET;
249 sa.sin_addr.s_addr = host_ip;
250 sa.sin_port = htons(tclink_port);
251
252 connect(sd, (struct sockaddr *) &sa, sizeof(sa));
253
254 return sd;
255}
256
257/* This function is called on a socket file descriptor once the connection has been
258 * established and we're ready to negotiate SSL. If the SSL handshake fails for some
259 * reason (such as the host on the other end not using SSL), it will return 0 for
260 * failure. Success returns 1.
261 */
262static int FinishConnection(TCLinkCon *c, int sd)
263{
264 int ssl_connected, is_error, errcode, res;
265 X509 *server_cert;
266 time_t start, remaining;
267 fd_set in, out, err;
268 struct timeval tv;
269
270 /* check if socket has connected successfully */
271 int val;
272 int /*socklen_t*/ size = 4;
273 getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)&val, &size);
274 if (val != 0)
275 return 0;
276
277 SSL_clear(c->ssl);
278
279 SSL_set_fd(c->ssl, sd);
280
281 ssl_connected = 0;
282 is_error = 0;
283 start = time(0);
284
285 while (!ssl_connected && !is_error)
286 {
287
288 remaining = 5 - (time(0) - start);
289 if (remaining <= 0) {
290 is_error = 1;
291 break;
292 }
293
294 res = SSL_connect(c->ssl);
295
296 ssl_connected = ((res == 1) && SSL_is_init_finished(c->ssl));
297
298 if (!ssl_connected)
299 {
300 FD_ZERO(&in); FD_SET((unsigned)sd, &in);
301 FD_ZERO(&out); FD_SET((unsigned)sd, &out);
302 FD_ZERO(&err); FD_SET((unsigned)sd, &err);
303 /* the documentation does not suggest that both error types occur at the same time so
304 * the retry logic will consume all the outstanding events
305 * we do not actually use oob data, but if it is sent, it is treated as an error all the
306 * same
307 */
308 errcode = SSL_get_error(c->ssl, res);
309 switch (errcode)
310 {
311 case SSL_ERROR_NONE:
312 /* no error, we should have a connection, check again */
313 break;
314
315 case SSL_ERROR_WANT_READ:
316 /* no error, just wait for more data */
317 tv.tv_sec = remaining; tv.tv_usec = 0;
318 /* posix-2001 says the function will modify the appropriate descriptors */
319 if (select(sd+1, &in, NULL, &err, &tv) < 0 ||
320 FD_ISSET((unsigned)sd, &err)
321 )
322 is_error = 1;
323 break;
324 case SSL_ERROR_WANT_WRITE:
325 /* no error, just wait for more data */
326 tv.tv_sec = remaining; tv.tv_usec = 0;
327 if (select(sd+1, NULL, &out, &err, &tv) < 0 ||
328 FD_ISSET((unsigned)sd, &err)
329 )
330 is_error = 1;
331 break;
332 case SSL_ERROR_ZERO_RETURN: /* peer closed the connection */
333 case SSL_ERROR_SSL: /* error in SSL handshake */
334 default:
335 is_error = 1;
336 }
337 }
338 }
339
340 if (is_error) {
341 return 0;
342 }
343
344
345#ifdef WIN32
346 u_long param = 0;
347 ioctlsocket(sd, FIONBIO, &param); // make the socket blocking again
348#else
349 fcntl(sd, F_SETFL, 0); /* make the socket blocking again */
350#endif
351
352 /* verify that server certificate is authentic */
353 server_cert = SSL_get_peer_certificate(c->ssl);
354 if (!server_cert) {
355 return 0;
356 }
357 if (c->validate_cert && c->validate_cert(0, server_cert) != 0)
358 {
359 X509_free(server_cert);
360 return 0;
361 }
362 X509_free(server_cert);
363
364 return 1;
365}
366
367/* This function should be called on list of socket file descriptors (sd) to determine
368 * if any have opened successfully. If so, it will return which one (index into
369 * the array). Otherwise it returns -1 if none have successfully opened.
370 * This function will block for a maximum of 3 seconds.
371 * As this function calls FinishConnection(), you shouldn't need to do anything special
372 * after it returns success - the socket is set up and ready for use.
373 */
374static int CheckConnection(TCLinkCon *c, int *sd, int num_sd)
375{
376 fd_set wr_set, err_set;
377 struct timeval tv;
378 int max_sd = -1, i;
379
380 tv.tv_sec = 3; /* wait 3 seconds for soc->mething to happen */
381 tv.tv_usec = 0;
382
383 /* build the fd_sets used for select() */
384 FD_ZERO(&wr_set);
385 FD_ZERO(&err_set);
386 for (i = 0; i < num_sd; i++)
387 {
388 if (sd[i] < 0) continue;
389 FD_SET(sd[i], &wr_set);
390 FD_SET(sd[i], &err_set);
391 if (sd[i] > max_sd)
392 max_sd = sd[i];
393 }
394
395 /* run the select and see what we have waiting for us */
396 if (select(max_sd + 1, NULL, &wr_set, &err_set, &tv) < 1)
397 return -1; /* I hope this never happens */
398
399 for (i = 0; i < num_sd; i++)
400 if (sd[i] >= 0)
401 {
402 if (FD_ISSET(sd[i], &err_set))
403 {
404 /* error - close the socket and mark it defunct */
405 close(sd[i]);
406 sd[i] = -1;
407 }
408 else if (FD_ISSET(sd[i], &wr_set))
409 {
410 /* socket has opened! try to negotiate SSL */
411 if (FinishConnection(c, sd[i])) {
412 /* socket is ready to go, so return success */
413 c->sd = sd[i];
414 return i;
415 }
416 else {
417 /* SSL handshake had errors, close the socket and mark it defunct */
418 close(sd[i]);
419 sd[i] = -1;
420 }
421 }
422 }
423
424 /* if we get here, nothing much interesting happened during those 3 seconds */
425 return -1;
426}
427
428void do_SSL_randomize()
429{
430 enum { RAND_VALS = 32 };
431 int randbuf[RAND_VALS];
432 char fname[512];
433 int use_rand_file;
434 time_t t;
435 int i, c;
436
437 /* if they have a /dev/urandom we can skip this function */
438 if (RAND_status() != 0)
439 return;
440
441 t = time(0);
442 RAND_seed((char *)&t, sizeof(time_t));
443
444 /* have they specified a random file with RANDFILE environment variable? */
445 use_rand_file = RAND_file_name(fname, sizeof(fname)) ? 1 : 0;
446 if (use_rand_file)
447 RAND_load_file(fname, 4096);
448
449 /* stuff it with packets of random numbers until it is satisfied */
450 for (i = 0; i < 256 && RAND_status() == 0; i++)
451 {
452 for (c = 0; c < RAND_VALS; c++)
453 randbuf[c] = rand();
454 RAND_seed((char *)randbuf, sizeof(int) * RAND_VALS);
455 }
456}
457
458/* Open a connection to one of the TrustCommerce gateway servers. */
459static int Connect(TCLinkCon *c, int host_hash)
460{
461 struct hostent default_he;
462 char *addr_list[3]; int addr[2];
463 struct hostent *he;
464 unsigned int **gw;
465
466 enum { MAX_HOSTS = 32 };
467 time_t last_connect[MAX_HOSTS];
468 int sd[MAX_HOSTS];
469 int num_sd = 0;
470 int host;
471
472 int i, j, sort, sort_val;
473
474 c->sd = -1;
475 c->is_error = 0;
476
477 srand(time(0));
478
479 /* These are used as BACKUP ONLY if the DNS if offline. */
480 addr[0] = inet_addr("207.38.46.42");
481 addr[1] = inet_addr("208.42.227.151");
482 addr_list[0] = (char *)&addr[0];
483 addr_list[1] = (char *)&addr[1];
484 addr_list[2] = 0;
485 default_he.h_addr_list = addr_list;
486
487 /* determine IP addresses of gateway */
488 if (!c->ip)
489 {
490 he = gethostbyname(tclink_host);
491 if (he)
492 c->dns = 1;
493 else {
494 /* fall back to hardcoded IPs in an emergency */
495 c->dns = 0;
496 he = &default_he;
497 }
498
499 for (c->num_ips = 0; he->h_addr_list[c->num_ips]; c->num_ips++)
500 ;
501
502 c->ip = (int *)malloc(c->num_ips * sizeof(int));
503 gw = (int unsigned **)he->h_addr_list;
504
505 /* sort the IP address list before storing it */
506 for (i = 0; i < c->num_ips; i++)
507 {
508 sort = 0; sort_val = *gw[0];
509 for (j = 1; j < c->num_ips; j++)
510 if (*gw[j] > sort_val)
511 {
512 sort = j;
513 sort_val = *gw[j];
514 }
515
516 c->ip[i] = sort_val;
517 *gw[sort] = 0;
518 }
519 }
520
521 /* do some SSL setup */
522 if (!c->meth)
523 {
524 do_SSL_randomize(); /* handle systems without /dev/urandom */
525 SSLeay_add_ssl_algorithms();
526 c->meth = SSLv23_client_method();
527 c->ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; // Disable all known SSL versions
528 }
529
530 if (!c->ctx)
531 {
532 int val;
533
534 c->ctx = SSL_CTX_new(c->meth);
535 if (!c->ctx) return 0;
536 /* set options */
537 if (c->ctx_options)
538 SSL_CTX_set_options(c->ctx, c->ctx_options);
539
540 if (!c->trusted_ca_pem)
541 {
542 int is_file = is_regular_file(TCLINK_CA_PATH);
543 val = SSL_CTX_load_verify_locations(c->ctx, is_file?TCLINK_CA_PATH:NULL, is_file?NULL:TCLINK_CA_PATH);
544 }
545 else
546 {
547 extern int SSL_CTX_load_verify_locations_mem(SSL_CTX*, const char *);
548 val = SSL_CTX_load_verify_locations_mem(c->ctx, c->trusted_ca_pem);
549 }
550
551 if (!val) return 0; // failed to populate cert store
552
553 /* turn on certificate chain validation */
554 SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, NULL);
555 }
556
557 if (!c->ssl)
558 {
559 c->ssl = SSL_new(c->ctx);
560 if (!c->ssl)
561 {
562 SSL_CTX_free(c->ctx);
563 return 0;
564 }
565 }
566
567 /* This loop works as follows:
568 * Grab the first host. Try to open a connection to it. If there was an
569 * error (host down or unreachable) go to the next one. If nothing has happened
570 * after 3 seconds, open a second socket (the first one is still open!) and try
571 * with the next fail-over host. Continue to do this for a maximum of MAX_HOSTS
572 * sockets, or until our TIMEOUT value runs out. We also keep track of how recently
573 * we tried to connect to a given host, so that we avoid saturating the machines
574 * in a heavy-load situation (which could be caused by anything from heavy internet
575 * lag between the local host and the TrustCommerce servers, to heavy load on the
576 * servers themselves due to half a million people trying to run credit card
577 * transactions in the same half second - unlikely, but certainly possible.)
578 */
579 c->start_time = time(0);
580 c->pass = 1;
581 memset(last_connect, 0, MAX_HOSTS * sizeof(time_t));
582 host = host_hash % c->num_ips;
583
584 for ( ; time(0) < (c->start_time + TIMEOUT); c->pass++)
585 {
586 /* retry the first host at least once */
587 if (c->pass > 2) host += 1;
588 if (host >= c->num_ips) host = 0;
589
590 /* only connect if we haven't tried this host before, or it's been a little
591 * while (note random modifier to help stagger network traffic) */
592 if (last_connect[host] == 0 ||
593 (time(0) - last_connect[host]) >= number(TIMEOUT / 4, TIMEOUT))
594 {
595 if (num_sd < MAX_HOSTS)
596 {
597 /* fire up a new connection to this host */
598 if (c->pass != 1)
599 last_connect[host] = time(0);
600
601 sd[num_sd] = BeginConnection(c, c->ip[host]);
602 if (sd[num_sd] >= 0)
603 num_sd++;
604 }
605 }
606
607 /* scan all current sockets and see if we've made a successful connection
608 * somewhere. note that this also includes SSL and all that sort of fun,
609 * so once it returns success, we're all done. */
610 if (num_sd > 0)
611 {
612 if (CheckConnection(c, sd, num_sd) >= 0)
613 {
614 /* Success: close all other file handles and return */
615 for (i = 0; i < num_sd; i++)
616 if (sd[i] >= 0 && sd[i] != c->sd)
617 close(sd[i]);
618
619 return 1;
620 }
621 }
622
623 usleep(1000); // sleep for 1 millisecond
624 }
625
626 return 0;
627}
628
629/* Send a chunk of data through a connection previously opened with Connect(). */
630static int Send(TCLinkCon *c, const char *string)
631{
632 if (SSL_write(c->ssl, string, strlen(string)) < 0)
633 return 0;
634
635 return 1;
636}
637
638/* Peel a line off the current input. Note that this DOESN'T necessarily wait for all
639 * input to come in, only up to a "\n". -1 is returned for a network error, otherwise
640 * it returns the length of the line read. If there is not a complete line pending
641 * for read this will block until there is, or an error occurs.
642 */
643static int ReadLine(TCLinkCon *c, char *buffer, char *destbuf)
644{
645 struct timeval tv;
646 fd_set read;
647 fd_set error;
648 int sel;
649
650 while (1) /* we wait for a line to come in or an error to occur */
651 {
652 char *eol = strchr(buffer, '\n');
653 if (eol != NULL)
654 {
655 /* peel off the line and return it */
656 *eol++ = 0;
657 safe_copy(destbuf, buffer, TC_LINE_MAX);
658 memmove(buffer, eol, strlen(eol)+1);
659 return strlen(destbuf);
660 }
661 else
662 {
663 if (c->is_error == 1)
664 return -1;
665
666 /* do socket work to grab the most recent chunk of incoming data */
667 FD_ZERO(&read); FD_SET(c->sd, &read);
668 FD_ZERO(&error); FD_SET(c->sd, &error);
669 tv.tv_sec = TIMEOUT;
670 tv.tv_usec = 0;
671
672 sel = select(c->sd+1, &read, NULL, &error, &tv);
673 if (sel < 1)
674 c->is_error = 1;
675 else if (FD_ISSET(c->sd, &error))
676 c->is_error = 1;
677 else if (FD_ISSET(c->sd, &read))
678 {
679 int buffer_end = strlen(buffer);
680 int size = SSL_read(c->ssl, buffer + buffer_end, TC_BUFF_MAX-1 - buffer_end);
681 if (size == 0)
682 {
683 int error_type = SSL_get_error(c->ssl, size);
684 switch (error_type)
685 {
686 /* this would never happen in practice */
687 case SSL_ERROR_NONE:
688 /* this wouldn't happen either because the ssl transport is blocking */
689 case SSL_ERROR_WANT_READ:
690 case SSL_ERROR_WANT_WRITE:
691 buffer[buffer_end] = 0;
692 break;
693
694 /* these others should not really happen but if they do, we bail */
695 /* we would never get any more data and it looks like the callee is expecting something */
696 case SSL_ERROR_ZERO_RETURN:
697 case SSL_ERROR_WANT_CONNECT:
698 case SSL_ERROR_WANT_ACCEPT:
699 case SSL_ERROR_SYSCALL:
700 case SSL_ERROR_WANT_X509_LOOKUP:
701 case SSL_ERROR_SSL:
702 default:
703 c->is_error = 1;
704 break;
705 }
706 }
707 else if (size < 0)
708 c->is_error = 1;
709 else
710 buffer[buffer_end + size] = 0;
711 }
712 }
713 }
714}
715
716/* Closes a connection opened with Connect() and frees memory associated with it.
717 * You ONLY need to Close() connections which opened successfully; those that don't
718 * clean up after themselves before Connect() returns.
719 */
720static int Close(TCLinkCon *c)
721{
722 if (c->ssl)
723 {
724 /* The full shutdown presented here is more for completeness than necessity; at this point in the
725 * application, we have already received the end trailer (or bust) which is generally accompanied by
726 * a close notify message. If the software chooses to respond to the close notify (per TLS specification)
727 * this would result in at least reading the incoming close notify and issuing our own. Because this entails
728 * an additional round trip that is not needed (the transaction is done after the accompanying END), there
729 * does not appear to be a benefit to it at all. By default though, this configuration is enabled and
730 * can be disabled by the integrator for performance reasons.
731 */
732 if (c->full_ssl_close)
733 {
734 int status = SSL_shutdown(c->ssl);
735 if (status == 0) status = SSL_shutdown(c->ssl);
736 }
737 else
738 SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
739 }
740
741 if (c->sd >= 0) {
742 close(c->sd);
743 c->sd = -1;
744 }
745
746 if (c->trusted_ca_pem) {
747 free(c->trusted_ca_pem);
748 c->trusted_ca_pem = NULL;
749 }
750
751 return 1;
752}
753
754static void stuff_string(char *buf, int *len, int size, const char *add)
755{
756 int newlen = strlen(add);
757 if ((*len + newlen) >= size)
758 newlen = size - *len - 1;
759 if (newlen < 1) return;
760 strncpy(buf + *len, add, newlen);
761 *len += newlen;
762 buf[*len] = 0;
763}
764
765/**********************************************
766 * API functions exported to the user client. *
767 **********************************************/
768
769TCLinkHandle TCLinkCreate()
770{
771 extern int TCLinkDefaultValidate(int, void *);
772
773 TCLinkCon *c = (TCLinkCon *)malloc(sizeof(TCLinkCon));
774
775 c->ip = NULL;
776 c->num_ips = 0;
777 c->sd = -1;
778
779 c->meth = NULL;
780 c->ctx = NULL;
781 c->ssl = NULL;
782
783 c->send_param_list = NULL;
784 c->send_param_tail = NULL;
785 c->recv_param_list = NULL;
786
787 c->is_error = 0;
788 c->pass = 0;
789 c->start_time = 0;
790 c->dns = -1;
791
792 c->trusted_ca_pem = NULL;
793 c->validate_cert = TCLinkDefaultValidate;
794 c->full_ssl_close = 1;
795
796 return (TCLinkHandle)c;
797}
798
799int TCLinkSetFullClose(TCLinkHandle handle, int full_ssl_close)
800{
801 TCLinkCon *c = (TCLinkCon *)handle;
802 int swap = c->full_ssl_close;
803 c->full_ssl_close = full_ssl_close ? 1 : 0;
804 return swap;
805}
806
807void TCLinkSetTrustedCABundle(TCLinkHandle handle, const char *str, int len)
808{
809 TCLinkCon *c = (TCLinkCon *)handle;
810
811 if (c->trusted_ca_pem)
812 free(c->trusted_ca_pem);
813
814 if (str == NULL)
815 {
816 c->trusted_ca_pem = NULL;
817 return;
818 }
819
820 c->trusted_ca_pem = malloc(len+1);
821 strncpy(c->trusted_ca_pem,str,len);
822 c->trusted_ca_pem[len] = 0;
823}
824
825void TCLinkSetValidateCallback(TCLinkHandle handle, int (*validate_cert)(int, void *))
826{
827 TCLinkCon *c = (TCLinkCon *)handle;
828 if (validate_cert == NULL)
829 {
830 extern int TCLinkDefaultValidate(int, void *);
831 c->validate_cert = TCLinkDefaultValidate;
832 }
833 else
834 c->validate_cert = validate_cert;
835}
836
837void TCLinkPushParam(TCLinkHandle handle, const char *name, const char *value)
838{
839 param *p;
840 char *ch;
841
842 TCLinkCon *c = (TCLinkCon *)handle;
843
844 if (name && value)
845 {
846 p = (param *)malloc(sizeof(param));
847 p->name = strdup(name);
848 p->value = strdup(value);
849 p->next = NULL;
850 if (c->send_param_tail)
851 c->send_param_tail->next = p;
852 else
853 c->send_param_list = p;
854 c->send_param_tail = p;
855
856 /* remove newlines and equals signs from the parameter name */
857 for (ch = p->name; *ch; ch++)
858 if (*ch == '=' || *ch == '\n') *ch = ' ';
859
860 /* remove newlines from the value */
861 for (ch = p->value; *ch; ch++)
862 if (*ch == '\n') *ch = ' ';
863 }
864}
865
866void TCLinkSend(TCLinkHandle handle)
867{
868 param *p, *next;
869 char buf[TC_BUFF_MAX], destbuf[TC_LINE_MAX];
870 char buf2[1024];
871 int host_hash = 1;
872 int retval = 0;
873
874 TCLinkCon *c = (TCLinkCon *)handle;
875
876 ClearRecvList(c);
877
878 /* build most of the string we will send to the processor */
879 sprintf(buf, "BEGIN\nversion=%s\n", tclink_version);
880
881 for (p = c->send_param_list; p; p = next)
882 {
883 next = p->next;
884 SAFE_COPY(buf2, p->name);
885 SAFE_APPEND(buf2, "=");
886 SAFE_APPEND(buf2, p->value);
887 SAFE_APPEND(buf2, "\n");
888 SAFE_APPEND(buf, buf2);
889 if (!strcasecmp(p->name, "custid")) {
890 host_hash = atoi(p->value);
891 host_hash = (host_hash / 100) + (host_hash % 100);
892 }
893 free(p->name);
894 free(p->value);
895 free(p);
896 }
897
898 c->send_param_list = c->send_param_tail = NULL;
899
900 /* try to make the connection */
901 if (!Connect(c, host_hash))
902 {
903 Close(c); /* clean up any memory Connect() may have left lying around */
904 AddRecvParam(c, "status", "error");
905 AddRecvParam(c, "errortype", "cantconnect");
906 return;
907 }
908
909 /* append some data about the connection */
910 sprintf(buf+strlen(buf), "pass=%d\ntime=%ld\n", c->pass, time(0) - c->start_time);
911 if (c->dns != 1) SAFE_APPEND(buf, "dns=n\n");
912 SAFE_APPEND(buf, "END\n");
913
914 /* send the data */
915 if (Send(c, buf))
916 {
917 int state = 0;
918 buf[0] = destbuf[0] = 0; /* recycle buf */
919 c->is_error = 0;
920 while (1)
921 {
922 int len = ReadLine(c, buf, destbuf);
923 if (len == 0) continue;
924 if (len < 0) break;
925 if (strcasecmp(destbuf, "BEGIN") == 0)
926 {
927 if (state != 0)
928 { state = -1; break; }
929 state = 1;
930 }
931 else if (strcasecmp(destbuf, "END") == 0)
932 {
933 state = (state != 1) ? -1 : 2;
934 break;
935 }
936 else
937 {
938 if (state != 1 || !AddRecvString(c, destbuf))
939 { state = -1; break; }
940 }
941 }
942 if (state == 2)
943 retval = 1;
944 }
945
946 Close(c);
947
948 if (!retval)
949 {
950 ClearRecvList(c);
951 AddRecvParam(c, "status", "error");
952 AddRecvParam(c, "errortype", "linkfailure");
953 }
954}
955
956char *TCLinkGetResponse(TCLinkHandle handle, const char *name, char *value)
957{
958 param *p;
959 TCLinkCon *c = (TCLinkCon *)handle;
960
961 for (p = c->recv_param_list; p; p = p->next)
962 if (strcasecmp(name, p->name) == 0)
963 {
964 safe_copy(value, p->value, PARAM_MAX_LEN);
965 return value;
966 }
967
968 return NULL;
969}
970
971char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size)
972{
973 param *p;
974 int len = 0;
975 TCLinkCon *c = (TCLinkCon *)handle;
976
977 for (p = c->recv_param_list; p; p = p->next) {
978 stuff_string(buf, &len, size, p->name);
979 stuff_string(buf, &len, size, "=");
980 stuff_string(buf, &len, size, p->value);
981 stuff_string(buf, &len, size, "\n");
982 }
983
984 return buf;
985}
986
987void TCLinkDestroy(TCLinkHandle handle)
988{
989 TCLinkCon *c = (TCLinkCon *)handle;
990 if (!c) return;
991
992 ClearSendList(c);
993 ClearRecvList(c);
994 Close(c);
995
996 if (c->ip)
997 free(c->ip);
998
999 if (c->ssl) {
1000 SSL_free(c->ssl);
1001 c->ssl = NULL;
1002 }
1003
1004 if (c->ctx) {
1005 SSL_CTX_free(c->ctx);
1006 c->ctx = NULL;
1007 }
1008
1009 free(c);
1010}
1011
1012char *TCLinkGetVersion(char *buf)
1013{
1014 strcpy(buf, tclink_version);
1015 return buf;
1016}
1017