tclink 4.2.0

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