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