tclink_gs 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }