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.
- checksums.yaml +7 -0
- data/LICENSE +504 -0
- data/README +69 -0
- data/Rakefile +21 -0
- data/doc/TCDevGuide.html +1923 -0
- data/doc/TCDevGuide.txt +2160 -0
- data/ext/config.h +2 -0
- data/ext/extconf.rb +53 -0
- data/ext/mem.c +166 -0
- data/ext/rb_tclink.c +84 -0
- data/ext/tclink.c +1018 -0
- data/ext/tclink.h +83 -0
- data/ext/validate.c +154 -0
- data/tclink.gemspec +33 -0
- data/test/tclink_test.rb +36 -0
- metadata +59 -0
data/ext/config.h
ADDED
data/ext/extconf.rb
ADDED
@@ -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")
|
data/ext/mem.c
ADDED
@@ -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
|
+
}
|
data/ext/rb_tclink.c
ADDED
@@ -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
|
+
}
|
data/ext/tclink.c
ADDED
@@ -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, ¶m);
|
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, ¶m); // 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
|
+
|