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