tclink_gs 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README ADDED
@@ -0,0 +1,98 @@
1
+ Some countries have regulations on the use of cryptographic libraries
2
+ like the ones embedded in TCLink. It may be unlawful to download TCLink
3
+ in these countries.
4
+
5
+
6
+ TCLink v4.5.0
7
+ Ruby Implementation
8
+ copyright (C) TrustCommerce 2015-2020
9
+ http://www.trustcommerce.com
10
+ techsupport@trustcommerce.com
11
+
12
+ February 18, 2020
13
+
14
+ I. DESCRIPTION
15
+
16
+ TCLink is a thin client library to allow your servers to
17
+ connect to the TrustCommerce payment gateway easily and consistently.
18
+ The protocol (which is the same across all platforms and languages) is
19
+ well-documented in the TC Link Developers Guide, so please consult it for
20
+ any questions you may have about the protocol syntax itself.
21
+
22
+
23
+ II. LICENSE
24
+
25
+ TCLink for Ruby is released under the GNU LGPL. Please read LICENSE
26
+ for details.
27
+
28
+
29
+ III. REQUIREMENTS
30
+
31
+ You need to have the OpenSSL development libraries installed. It
32
+ is recommended that you use the latest version provided by the vendor
33
+ for PCI reasons.
34
+
35
+ Besides the normal Ruby install, you'll need the ruby-devel package,
36
+ which contains files needed for building Ruby extensions.
37
+
38
+
39
+ IV. BUILDING
40
+
41
+ At the root directory of this archive, execute the following:
42
+
43
+ ./build.sh
44
+
45
+ Notes:
46
+ See below regarding Mac OS X and Homebrew.
47
+
48
+ If the module builds without errors, test it with this command:
49
+
50
+ ruby tctest.rb
51
+
52
+ This script will run a test transaction and print the results.
53
+
54
+
55
+ V. INSTALLATION
56
+
57
+ If you have root access to the machine, you will probably want to
58
+ install TCLink as a global extension. You can do this by copying the
59
+ extension library (tclink.so) to your Ruby extensions directory, which
60
+ is typically somewhere under /usr/lib/ruby, such as
61
+ /usr/lib/ruby/1.6/i386-linux.
62
+
63
+ If you can't or don't want to install the module system wide, you can
64
+ still use in a script by adding an absolute or relative path to the
65
+ require 'tclink' invocation. For example:
66
+
67
+ require '/home/user/tclink'
68
+
69
+
70
+ VI. USAGE
71
+
72
+ The tctest.rb script shows a simple example of running transactions
73
+ through the TCLink API. For further information, please consult the TC
74
+ Developer's Guide, located in the doc subdirectory.
75
+
76
+ VII. PLATFORMS
77
+
78
+ The included code has been tested on the following platforms:
79
+
80
+ CentOS Linux release 8.1.1911 (Core)
81
+ OpenSSL Version 1.1.1c-2 (Distribution), Ruby Version 2.6.5 (From Source)
82
+ OpenSSL Version 1.1.1c-2 (Distribution), ruby-2.5.5-105.module_el8.1.0+214+9be47fd7.x86_64 (Distribution), redhat-rpm-config-120-1.el8.noarch (Distribution)
83
+ Debian Linux release 8.11
84
+ OpenSSL Version 1.0.1t-1+deb8u12 (Distribution), Ruby Version 2.1.5-2+deb8u7 (Distribution)
85
+ Debian Linux release 9.8
86
+ OpenSSL Version 1.1.0l-1~deb9u1 (Distribution), Ruby Version 2.3.3-1+deb9u7 (Distribution)
87
+ Debian Linux release 10.3
88
+ OpenSSL Version 1.1.1d-0+deb10u2 (Distribution), Ruby Version 2.5.5-3+deb10u1 (Distribution)
89
+ Mac OS 10.13.6
90
+ OpenSSL Version 1.1.1d (via Homebrew)
91
+ "./build.sh -d=/usr/local/etc/openssl@1.1/cert.pem"
92
+
93
+
94
+
95
+
96
+ It should work on most modern UNIXes. If you need assistance getting
97
+ it running on your platform, please email techsupport@trustcommerce.com.
98
+
@@ -0,0 +1,36 @@
1
+ require 'mkmf'
2
+
3
+ File.delete 'config.h' if File.exist? 'config.h'
4
+
5
+ ssldir = (`openssl version -d`.chomp.split(/:/)[1]).gsub(' ', '').gsub(/"/, '')
6
+ certdir = "#{ssldir}/certs"
7
+ certpath = ENV['SSL_CERT_PATH']
8
+
9
+ if certpath.nil?
10
+ filenames = %w(ca-bundle.crt ca-certificates.crt ca-bundle.trust.crt tls-ca-bundle.pem cert.pem)
11
+ dirs = [ssldir, certdir]
12
+
13
+ filenames.each do |name|
14
+ dirs.each do |dir|
15
+ path = "#{dir}/#{name}"
16
+ if File.exist?(path) & certpath.nil?
17
+ certpath = path
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ # determine the platform name
24
+ uname=`uname -sm | tr ' ' -`.chomp
25
+
26
+ tclink_version = '4.5.0'
27
+
28
+ # config.h file sets TCLINK_VERSION with approperiate environment information
29
+ File.open("config.h", "w") do |f|
30
+ f.puts "#define TCLINK_VERSION \"#{tclink_version}-Ruby-#{uname}\""
31
+ f.puts "#define TCLINK_CA_PATH \"#{certpath}\""
32
+ end
33
+
34
+ have_library("crypto", "CRYPTO_lock")
35
+ have_library("ssl", "SSL_connect")
36
+ create_makefile("tclink/tclink")
@@ -0,0 +1,147 @@
1
+ /**
2
+ * OpenSSL uses many data structures on which operations must be atomic.
3
+ * OpenSSL provides for the thread safety of its data structures by requiring
4
+ * each thread to acquire a mutually exclusive lock known as a mutex that
5
+ * protects the structure before allowing it to be accessed.
6
+ * When the thread is finished with the data structure, it releases the mutex,
7
+ * allowing another thread to acquire the lock and access the data structure.
8
+ * OpenSSL requires the application programmer to perform these operations in
9
+ * a manner appropriate for the platform it's running on by making callbacks to
10
+ * functions that the application registers with OpenSSL for this purpose.
11
+ *
12
+ * Use following link for additional details:
13
+ * 'https://www.openssl.org/docs/man1.0.1/crypto/threads.html'
14
+ * 'https://wiki.openssl.org/index.php/Library_Initialization'
15
+
16
+ * Following code defines and utilizes global mutex array required to make
17
+ * OpenSSL thread safe. It was adapted from an example
18
+ * 'crypto/threads/mttest.c' provided with the OpenSSL package.
19
+ *
20
+ */
21
+
22
+ #include <assert.h>
23
+ #include <openssl/crypto.h>
24
+ #include <openssl/err.h>
25
+ #include <openssl/ssl.h>
26
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
27
+ #include <pthread.h>
28
+ #endif
29
+
30
+ void __attribute__((constructor)) TCLink_OpenSSLInit(void);
31
+ void __attribute__((destructor)) TCLink_OpenSSLCleanup(void);
32
+
33
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
34
+ static pthread_mutex_t* lock_cs;
35
+ static long* lock_count;
36
+
37
+ static void
38
+ TCLink_pthreads_thread_id(CRYPTO_THREADID* tid)
39
+ {
40
+ CRYPTO_THREADID_set_numeric(tid, (unsigned long)pthread_self());
41
+ }
42
+
43
+ static void
44
+ TCLink_pthreads_locking_callback(int mode, int type, const char* file, int line)
45
+ {
46
+ if (mode & CRYPTO_LOCK) {
47
+ pthread_mutex_lock(&(lock_cs[type]));
48
+ lock_count[type]++;
49
+ } else {
50
+ pthread_mutex_unlock(&(lock_cs[type]));
51
+ }
52
+ }
53
+
54
+ static void
55
+ TCLink_thread_setup(void)
56
+ {
57
+ int i = 0;
58
+ lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
59
+ lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
60
+
61
+ if (!lock_cs || !lock_count) {
62
+ /* Nothing we can do about this...void function! */
63
+ if (lock_cs) {
64
+ OPENSSL_free(lock_cs);
65
+ lock_cs = NULL;
66
+ }
67
+
68
+ if (lock_count) {
69
+ OPENSSL_free(lock_count);
70
+ lock_count = NULL;
71
+ }
72
+
73
+ return;
74
+ }
75
+
76
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
77
+ lock_count[i] = 0;
78
+ pthread_mutex_init(&(lock_cs[i]), NULL);
79
+ }
80
+
81
+ CRYPTO_THREADID_set_callback(TCLink_pthreads_thread_id);
82
+ CRYPTO_set_locking_callback(TCLink_pthreads_locking_callback);
83
+ }
84
+
85
+ static void
86
+ TCLink_thread_cleanup(void)
87
+ {
88
+ int i = 0;
89
+
90
+ if (!lock_cs || !lock_count)
91
+ return;
92
+
93
+ CRYPTO_set_locking_callback(NULL);
94
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
95
+ pthread_mutex_destroy(&(lock_cs[i]));
96
+ }
97
+
98
+ OPENSSL_free(lock_cs);
99
+ OPENSSL_free(lock_count);
100
+ }
101
+
102
+ #endif
103
+ /**
104
+ *
105
+ * Initialize the OpenSSL library.
106
+ * Also sets up static callback functions required for multi-thread safety.
107
+ */
108
+ void
109
+ TCLink_OpenSSLInit(void)
110
+ {
111
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
112
+ SSL_load_error_strings();
113
+ assert(SSL_library_init() == 1);
114
+ TCLink_thread_setup();
115
+ #else
116
+ assert(OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
117
+ OPENSSL_INIT_ADD_ALL_CIPHERS |
118
+ OPENSSL_INIT_ADD_ALL_DIGESTS |
119
+ OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_ASYNC |
120
+ #ifdef OPENSSL_INIT_NO_ATEXIT
121
+ OPENSSL_INIT_NO_ATEXIT |
122
+ #endif
123
+ #ifdef OPENSSL_INIT_ATFORK
124
+ OPENSSL_INIT_ATFORK |
125
+ #endif
126
+ OPENSSL_INIT_LOAD_SSL_STRINGS,
127
+ NULL) == 1);
128
+ #endif
129
+ }
130
+
131
+ /**
132
+ *
133
+ * De-initializes the OpenSSL library.
134
+ * Performs cleanup required for global data structures.
135
+ */
136
+ void
137
+ TCLink_OpenSSLCleanup(void)
138
+ {
139
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
140
+ TCLink_thread_cleanup();
141
+ EVP_cleanup();
142
+ CRYPTO_cleanup_all_ex_data();
143
+ ERR_free_strings();
144
+ #else
145
+ OPENSSL_cleanup();
146
+ #endif
147
+ }
@@ -0,0 +1,65 @@
1
+ /* ruby_tclink.c - Library code for the TCLink client API. */
2
+
3
+
4
+ #include <ruby.h>
5
+ #include "tclink.h"
6
+
7
+ static VALUE
8
+ tclink_getversion(VALUE obj) {
9
+ return rb_str_new2(TCLINK_VERSION);
10
+ }
11
+
12
+ static VALUE
13
+ tclink_send(VALUE obj, VALUE params) {
14
+ TCLinkHandle handle;
15
+ char buf[4096];
16
+ VALUE input_keys, input_key, input_value, result;
17
+ char *result_key, *result_value, *next_result_key;
18
+ int input_keys_count;
19
+ int i = 0;
20
+
21
+ handle = TCLinkCreate();
22
+
23
+ /* grab the Ruby hash and stuff each parameter set into TCLink */
24
+ input_keys = rb_funcall(params, rb_intern("keys"), 0, 0);
25
+ input_keys_count = FIX2INT(rb_funcall(input_keys, rb_intern("length"),
26
+ 0, 0));
27
+
28
+ for (i = 0; i < input_keys_count; i++) {
29
+ input_key = rb_funcall(input_keys, rb_intern("[]"), 1,
30
+ INT2FIX(i));
31
+ input_value = rb_hash_aref(params, input_key);
32
+ TCLinkPushParam(handle, RSTRING_PTR(StringValue(input_key)),
33
+ RSTRING_PTR(StringValue(input_value)));
34
+ }
35
+
36
+ /* send the transaction */
37
+ TCLinkSend(handle);
38
+
39
+ /* pull out the returned parameters and put them in a Ruby hash */
40
+ TCLinkGetEntireResponse(handle, buf, sizeof(buf));
41
+
42
+ result = rb_hash_new();
43
+ result_key = result_value = buf;
44
+ while (result_key && (result_value = strchr(result_key, '='))) {
45
+ *result_value++ = 0;
46
+ next_result_key = strchr(result_value, '\n');
47
+ if (next_result_key) *next_result_key++ = 0;
48
+ rb_hash_aset(result, rb_str_new2(result_key),
49
+ rb_str_new2(result_value));
50
+ result_key = next_result_key;
51
+ }
52
+
53
+ TCLinkDestroy(handle);
54
+
55
+ /* return the results hash */
56
+ return result;
57
+ }
58
+
59
+ void
60
+ Init_tclink() {
61
+ VALUE tclink = rb_define_module("TCLink");
62
+
63
+ rb_define_module_function(tclink, "getVersion", tclink_getversion, 0);
64
+ rb_define_module_function(tclink, "send", tclink_send, 1);
65
+ }
@@ -0,0 +1,1070 @@
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
+
52
+ char tclink_version[] =
53
+ TCLINK_VERSION; /* TCLINK_VERSION is defined in Makefile */
54
+ char tclink_host[] = DEFAULT_HOST;
55
+ int tclink_port = 443;
56
+
57
+ /*************************************************/
58
+ /* Data structures used only within this module. */
59
+ /*************************************************/
60
+
61
+ /* Variables used for transaction data. */
62
+
63
+ typedef struct param_data
64
+ {
65
+ char* name;
66
+ char* value;
67
+ struct param_data* next;
68
+ } param;
69
+
70
+ typedef 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. */
103
+ static int
104
+ number(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 */
111
+ int
112
+ is_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
+
123
+ static bool
124
+ safe_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
+
136
+ static bool
137
+ safe_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. */
155
+ static void
156
+ AddRecvParam(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. */
171
+ static int
172
+ AddRecvString(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. */
185
+ static void
186
+ ClearSendList(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. */
200
+ static void
201
+ ClearRecvList(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
+
214
+ void
215
+ do_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
+ */
249
+ static int
250
+ init_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
+ */
314
+ static int
315
+ BeginConnection(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
+ */
346
+ static int
347
+ FinishConnection(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
+ */
465
+ static int
466
+ CheckConnection(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. */
521
+ static int
522
+ Connect(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
+ */
681
+ static int
682
+ Send(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
+ */
696
+ static int
697
+ ReadLine(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
+ */
772
+ static int
773
+ Close(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
+
803
+ static void
804
+ stuff_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
+
820
+ TCLinkHandle
821
+ TCLinkCreate()
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
+
851
+ int
852
+ TCLinkSetFullClose(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
+
860
+ void
861
+ TCLinkSetValidateCallback(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
+
871
+ void
872
+ TCLinkPushParam(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
+
902
+ void
903
+ TCLinkSend(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
+
1006
+ char*
1007
+ TCLinkGetResponse(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
+
1021
+ char*
1022
+ TCLinkGetEntireResponse(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
+
1038
+ void
1039
+ TCLinkDestroy(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
+
1065
+ char*
1066
+ TCLinkGetVersion(char* buf)
1067
+ {
1068
+ strcpy(buf, tclink_version);
1069
+ return buf;
1070
+ }