ruby-oci8 2.2.0.2 → 2.2.12
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/.yardopts +1 -6
- data/ChangeLog +600 -0
- data/NEWS +426 -35
- data/README.md +27 -9
- data/dist-files +13 -2
- data/docs/bind-array-to-in_cond.md +38 -0
- data/docs/conflicts-local-connections-and-processes.md +98 -0
- data/docs/hanging-after-inactivity.md +63 -0
- data/docs/install-binary-package.md +15 -11
- data/docs/install-full-client.md +18 -21
- data/docs/install-instant-client.md +45 -27
- data/docs/install-on-osx.md +31 -117
- data/docs/ldap-auth-and-function-interposition.md +123 -0
- data/docs/number-type-mapping.md +79 -0
- data/docs/platform-specific-issues.md +17 -50
- data/docs/report-installation-issue.md +11 -8
- data/docs/timeout-parameters.md +94 -0
- data/ext/oci8/apiwrap.c.tmpl +2 -5
- data/ext/oci8/apiwrap.rb +6 -1
- data/ext/oci8/apiwrap.yml +39 -143
- data/ext/oci8/attr.c +4 -2
- data/ext/oci8/bind.c +421 -9
- data/ext/oci8/connection_pool.c +3 -3
- data/ext/oci8/encoding.c +5 -5
- data/ext/oci8/env.c +8 -2
- data/ext/oci8/error.c +24 -16
- data/ext/oci8/extconf.rb +35 -63
- data/ext/oci8/hook_funcs.c +274 -61
- data/ext/oci8/lob.c +31 -75
- data/ext/oci8/metadata.c +8 -6
- data/ext/oci8/object.c +119 -29
- data/ext/oci8/oci8.c +46 -133
- data/ext/oci8/oci8.h +40 -123
- data/ext/oci8/oci8lib.c +178 -46
- data/ext/oci8/ocihandle.c +37 -37
- data/ext/oci8/ocinumber.c +24 -35
- data/ext/oci8/oraconf.rb +168 -337
- data/ext/oci8/oradate.c +19 -19
- data/ext/oci8/plthook.h +10 -0
- data/ext/oci8/plthook_elf.c +433 -268
- data/ext/oci8/plthook_osx.c +40 -9
- data/ext/oci8/plthook_win32.c +16 -1
- data/ext/oci8/stmt.c +52 -17
- data/ext/oci8/win32.c +4 -22
- data/lib/oci8/bindtype.rb +10 -17
- data/lib/oci8/check_load_error.rb +57 -10
- data/lib/oci8/compat.rb +5 -1
- data/lib/oci8/connection_pool.rb +74 -3
- data/lib/oci8/cursor.rb +70 -31
- data/lib/oci8/metadata.rb +9 -1
- data/lib/oci8/object.rb +14 -1
- data/lib/oci8/oci8.rb +184 -58
- data/lib/oci8/ocihandle.rb +0 -16
- data/lib/oci8/oracle_version.rb +11 -1
- data/lib/oci8/properties.rb +55 -0
- data/lib/oci8/version.rb +1 -1
- data/lib/oci8.rb +48 -4
- data/lib/ruby-oci8.rb +1 -0
- data/pre-distclean.rb +1 -3
- data/ruby-oci8.gemspec +4 -9
- data/setup.rb +11 -2
- data/test/README.md +37 -0
- data/test/config.rb +8 -1
- data/test/setup_test_object.sql +42 -14
- data/test/setup_test_package.sql +59 -0
- data/test/test_all.rb +4 -0
- data/test/test_bind_array.rb +70 -0
- data/test/test_bind_boolean.rb +99 -0
- data/test/test_bind_integer.rb +47 -0
- data/test/test_break.rb +11 -9
- data/test/test_clob.rb +5 -17
- data/test/test_connstr.rb +142 -0
- data/test/test_datetime.rb +8 -3
- data/test/test_metadata.rb +2 -1
- data/test/test_object.rb +99 -18
- data/test/test_oci8.rb +170 -46
- data/test/test_oranumber.rb +12 -6
- data/test/test_package_type.rb +17 -3
- data/test/test_properties.rb +17 -0
- metadata +45 -55
- data/docs/osx-install-dev-tools.png +0 -0
- data/test/README +0 -42
data/ext/oci8/extconf.rb
CHANGED
@@ -25,77 +25,44 @@ $CFLAGS += oraconf.cflags
|
|
25
25
|
saved_libs = $libs
|
26
26
|
$libs += oraconf.libs
|
27
27
|
|
28
|
-
oci_actual_client_version = 0x08000000
|
29
|
-
funcs = {}
|
30
|
-
YAML.load(open(File.dirname(__FILE__) + '/apiwrap.yml')).each do |key, val|
|
31
|
-
key = key[0..-4] if key[-3..-1] == '_nb'
|
32
|
-
ver = val[:version]
|
33
|
-
ver_major = (ver / 100)
|
34
|
-
ver_minor = (ver / 10) % 10
|
35
|
-
ver_update = ver % 10
|
36
|
-
ver = ((ver_major << 24) | (ver_minor << 20) | (ver_update << 12))
|
37
|
-
funcs[ver] ||= []
|
38
|
-
funcs[ver] << key
|
39
|
-
end
|
40
|
-
|
41
28
|
saved_defs = $defs.clone
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
29
|
+
fmt = "%s"
|
30
|
+
def fmt.%(x)
|
31
|
+
x ? super : "failed"
|
32
|
+
end
|
33
|
+
oci_major_version = checking_for 'OCI_MAJOR_VERSION in oci.h', fmt do
|
34
|
+
try_constant('OCI_MAJOR_VERSION', 'oci.h')
|
35
|
+
end
|
36
|
+
if oci_major_version
|
37
|
+
oci_minor_version = checking_for 'OCI_MINOR_VERSION in oci.h', fmt do
|
38
|
+
try_constant('OCI_MINOR_VERSION', 'oci.h')
|
39
|
+
end
|
40
|
+
else
|
41
|
+
if have_func('OCILobGetLength2')
|
42
|
+
oci_major_version = 10
|
43
|
+
oci_minor_version = 1
|
44
|
+
elsif have_func('OCIStmtPrepare2')
|
45
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 9iR2. Use ruby-oci8 2.1.x instead."
|
46
|
+
elsif have_func('OCILogon2')
|
47
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 9iR1. Use ruby-oci8 2.1.x instead."
|
48
|
+
elsif have_func('OCIEnvCreate')
|
49
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 8i. Use ruby-oci8 2.0.x instead."
|
50
|
+
else
|
51
|
+
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support Oracle 8. Use ruby-oci8 2.0.x instead."
|
54
52
|
end
|
55
|
-
puts "checking for Oracle #{verstr} API - #{result}"
|
56
|
-
break if result == 'fail'
|
57
53
|
end
|
58
54
|
$defs = saved_defs
|
59
55
|
|
60
|
-
have_type('oratext', 'ociap.h')
|
61
|
-
have_type('OCIDateTime*', 'ociap.h')
|
62
|
-
have_type('OCIInterval*', 'ociap.h')
|
63
|
-
have_type('OCICallbackLobRead2', 'ociap.h')
|
64
|
-
have_type('OCICallbackLobWrite2', 'ociap.h')
|
65
|
-
have_type('OCIAdmin*', 'ociap.h')
|
66
|
-
have_type('OCIAuthInfo*', 'ociap.h')
|
67
|
-
have_type('OCIMsg*', 'ociap.h')
|
68
|
-
have_type('OCICPool*', 'ociap.h')
|
69
|
-
|
70
56
|
if with_config('oracle-version')
|
71
|
-
|
57
|
+
oraver = OCI8::OracleVersion.new(with_config('oracle-version'))
|
72
58
|
else
|
73
|
-
|
59
|
+
oraver = OCI8::OracleVersion.new(oci_major_version, oci_minor_version)
|
74
60
|
end
|
75
|
-
$defs << "-DORACLE_CLIENT_VERSION=#{format('0x%08x',
|
61
|
+
$defs << "-DORACLE_CLIENT_VERSION=#{format('0x%08x', oraver.to_i)}"
|
76
62
|
|
77
63
|
if with_config('runtime-check')
|
78
64
|
$defs << "-DRUNTIME_API_CHECK=1"
|
79
65
|
$libs = saved_libs
|
80
|
-
else
|
81
|
-
oraver = OCI8::OracleVersion.new(oci_client_version)
|
82
|
-
if oraver < OCI8::OracleVersion.new(10)
|
83
|
-
case "#{oraver.major}.#{oraver.minor}"
|
84
|
-
when "8.0"
|
85
|
-
ora_name = "Oracle 8"
|
86
|
-
oci8_ver = "2.0.x"
|
87
|
-
when "8.1"
|
88
|
-
ora_name = "Oracle 8i"
|
89
|
-
oci8_ver = "2.0.x"
|
90
|
-
when "9.1"
|
91
|
-
ora_name = "Oracle 9iR1"
|
92
|
-
oci8_ver = "2.1.x"
|
93
|
-
when "9.2"
|
94
|
-
ora_name = "Oracle 9iR2"
|
95
|
-
oci8_ver = "2.1.x"
|
96
|
-
end
|
97
|
-
raise "Ruby-oci8 #{OCI8::VERSION} doesn't support #{ora_name}. Use ruby-oci8 #{oci8_ver} instead."
|
98
|
-
end
|
99
66
|
end
|
100
67
|
|
101
68
|
$objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
|
@@ -105,7 +72,7 @@ $objs = ["oci8lib.o", "env.o", "error.o", "oci8.o", "ocihandle.o",
|
|
105
72
|
"ocinumber.o", "ocidatetime.o", "object.o", "apiwrap.o",
|
106
73
|
"encoding.o", "oranumber_util.o", "thread_util.o", "util.o"]
|
107
74
|
|
108
|
-
if RUBY_PLATFORM =~ /mswin32|cygwin|
|
75
|
+
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw/
|
109
76
|
$defs << "-DUSE_WIN32_C"
|
110
77
|
$objs << "win32.o"
|
111
78
|
end
|
@@ -172,6 +139,8 @@ when 'rbx'
|
|
172
139
|
so_basename += 'rbx'
|
173
140
|
when 'jruby'
|
174
141
|
raise "Ruby-oci8 doesn't support jruby because its C extension support is in development in jruby 1.6 and deprecated in jruby 1.7."
|
142
|
+
when 'truffleruby'
|
143
|
+
so_basename += 'truffleruby'
|
175
144
|
else
|
176
145
|
raise 'unsupported ruby engine: ' + RUBY_ENGINE
|
177
146
|
end
|
@@ -179,19 +148,22 @@ end
|
|
179
148
|
print "checking for plthook... "
|
180
149
|
STDOUT.flush
|
181
150
|
case RUBY_PLATFORM
|
182
|
-
when /mswin32|cygwin|
|
151
|
+
when /mswin32|cygwin|mingw/
|
183
152
|
plthook_src = "plthook_win32.c"
|
184
153
|
when /darwin/
|
185
154
|
plthook_src = "plthook_osx.c"
|
186
155
|
else
|
187
156
|
plthook_src = "plthook_elf.c"
|
188
157
|
end
|
189
|
-
|
190
|
-
|
158
|
+
FileUtils.copy(File.dirname(__FILE__) + "/" + plthook_src, CONFTEST_C)
|
159
|
+
if xsystem(cc_command(""))
|
160
|
+
FileUtils.rm_f("#{CONFTEST}.#{$OBJEXT}")
|
191
161
|
puts plthook_src
|
192
162
|
$objs << plthook_src.gsub(/\.c$/, '.o')
|
193
163
|
$objs << "hook_funcs.o"
|
194
164
|
$defs << "-DHAVE_PLTHOOK"
|
165
|
+
have_library('dbghelp', 'ImageDirectoryEntryToData', ['windows.h', 'dbghelp.h']) if RUBY_PLATFORM =~ /cygwin/
|
166
|
+
$libs += ' -lws2_32' if RUBY_PLATFORM =~ /cygwin/
|
195
167
|
else
|
196
168
|
puts "no"
|
197
169
|
end
|
data/ext/oci8/hook_funcs.c
CHANGED
@@ -2,29 +2,69 @@
|
|
2
2
|
/*
|
3
3
|
* hook.c
|
4
4
|
*
|
5
|
-
* Copyright (C) 2015 Kubo Takehiro <kubo@jiubao.org>
|
5
|
+
* Copyright (C) 2015-2018 Kubo Takehiro <kubo@jiubao.org>
|
6
6
|
*/
|
7
|
+
#if defined(_WIN32) || defined(__CYGWIN__)
|
8
|
+
#define WINDOWS
|
9
|
+
#include <winsock2.h>
|
10
|
+
#endif
|
7
11
|
#include "oci8.h"
|
8
12
|
#include "plthook.h"
|
9
|
-
#
|
13
|
+
#ifdef __CYGWIN__
|
14
|
+
#undef boolean /* boolean defined in oratypes.h coflicts with that in windows.h */
|
15
|
+
#define stricmp strcasecmp
|
16
|
+
#define strnicmp strncasecmp
|
17
|
+
#endif
|
18
|
+
#ifdef WINDOWS
|
19
|
+
#include <windows.h>
|
20
|
+
#include <mstcpip.h>
|
21
|
+
#include <tlhelp32.h>
|
22
|
+
#else
|
10
23
|
#include <unistd.h>
|
11
24
|
#include <sys/socket.h>
|
25
|
+
#include <netinet/in.h>
|
26
|
+
#include <netinet/tcp.h>
|
27
|
+
#include <dlfcn.h>
|
12
28
|
#endif
|
13
29
|
|
14
|
-
#
|
15
|
-
|
16
|
-
#ifdef WIN32
|
30
|
+
#ifdef WINDOWS
|
17
31
|
static CRITICAL_SECTION lock;
|
18
32
|
#define LOCK(lock) EnterCriticalSection(lock)
|
19
33
|
#define UNLOCK(lock) LeaveCriticalSection(lock)
|
34
|
+
typedef int socklen_t;
|
20
35
|
#else
|
21
36
|
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
22
37
|
#define LOCK(lock) pthread_mutex_lock(lock)
|
23
38
|
#define UNLOCK(lock) pthread_mutex_unlock(lock)
|
24
39
|
#define SOCKET int
|
25
40
|
#define INVALID_SOCKET (-1)
|
41
|
+
#define WSAAPI
|
42
|
+
#endif
|
43
|
+
|
44
|
+
#if defined(__APPLE__) && defined(TCP_KEEPALIVE) /* macOS */
|
45
|
+
#define USE_TCP_KEEPALIVE
|
46
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
47
|
+
#elif defined(__sun) && defined(TCP_KEEPALIVE_THRESHOLD) /* Solaris */
|
48
|
+
#define USE_TCP_KEEPALIVE_THRESHOLD
|
49
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
50
|
+
#elif defined(TCP_KEEPIDLE) /* Linux, etc */
|
51
|
+
#define USE_TCP_KEEPIDLE
|
52
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
53
|
+
#elif defined(WINDOWS)
|
54
|
+
#define SUPPORT_TCP_KEEPALIVE_TIME
|
26
55
|
#endif
|
27
56
|
|
57
|
+
int oci8_cancel_read_at_exit = 0;
|
58
|
+
|
59
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
60
|
+
int oci8_tcp_keepalive_time = 0;
|
61
|
+
static int WSAAPI hook_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
62
|
+
#else
|
63
|
+
int oci8_tcp_keepalive_time = -1;
|
64
|
+
#endif
|
65
|
+
|
66
|
+
static char hook_errmsg[512];
|
67
|
+
|
28
68
|
typedef struct {
|
29
69
|
const char *func_name;
|
30
70
|
void *func_addr;
|
@@ -60,62 +100,56 @@ static void socket_entry_clear(socket_entry_t *entry)
|
|
60
100
|
UNLOCK(&lock);
|
61
101
|
}
|
62
102
|
|
63
|
-
static int replace_functions(
|
103
|
+
static int replace_functions(void *addr, const char *file, hook_func_entry_t *functions)
|
64
104
|
{
|
105
|
+
plthook_t *ph;
|
65
106
|
int i;
|
107
|
+
int rv = 0;
|
66
108
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
plthook_close(ph);
|
84
|
-
rb_raise(rb_eRuntimeError, "Could not replace function %s in %s", function->func_name, file);
|
85
|
-
}
|
109
|
+
if (plthook_open_by_address(&ph, addr) != 0) {
|
110
|
+
strncpy(hook_errmsg, plthook_error(), sizeof(hook_errmsg) - 1);
|
111
|
+
hook_errmsg[sizeof(hook_errmsg) - 1] = '\0';
|
112
|
+
return -1;
|
113
|
+
}
|
114
|
+
|
115
|
+
/* install hooks */
|
116
|
+
for (i = 0; functions[i].func_name != NULL ; i++) {
|
117
|
+
hook_func_entry_t *function = &functions[i];
|
118
|
+
rv = plthook_replace(ph, function->func_name, function->func_addr, &function->old_func_addr);
|
119
|
+
if (rv != 0) {
|
120
|
+
strncpy(hook_errmsg, plthook_error(), sizeof(hook_errmsg) - 1);
|
121
|
+
hook_errmsg[sizeof(hook_errmsg) - 1] = '\0';
|
122
|
+
while (--i >= 0) {
|
123
|
+
/*restore hooked fuction address */
|
124
|
+
plthook_replace(ph, functions[i].func_name, functions[i].old_func_addr, NULL);
|
86
125
|
}
|
87
|
-
|
88
|
-
|
126
|
+
snprintf(hook_errmsg, sizeof(hook_errmsg), "Could not replace function %s in %s", function->func_name, file);
|
127
|
+
break;
|
89
128
|
}
|
90
129
|
}
|
91
|
-
|
130
|
+
plthook_close(ph);
|
131
|
+
return rv;
|
92
132
|
}
|
93
133
|
|
94
|
-
#ifdef
|
134
|
+
#ifdef WINDOWS
|
135
|
+
|
136
|
+
#ifndef _MSC_VER
|
137
|
+
/* setsockopt() in ws2_32.dll */
|
138
|
+
#define setsockopt rboci_setsockopt
|
139
|
+
typedef int (WSAAPI *setsockopt_t)(SOCKET, int, int, const void *, int);
|
140
|
+
static setsockopt_t setsockopt;
|
141
|
+
#endif
|
142
|
+
|
143
|
+
/* system-wide keepalive interval */
|
144
|
+
static DWORD keepalive_interval;
|
95
145
|
|
96
146
|
static int locK_is_initialized;
|
97
147
|
|
98
148
|
static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
|
99
149
|
|
100
|
-
static const char * const tcp_func_files[] = {
|
101
|
-
/* full client */
|
102
|
-
"orantcp12.dll",
|
103
|
-
"orantcp11.dll",
|
104
|
-
"orantcp10.dll",
|
105
|
-
"orantcp9.dll",
|
106
|
-
/* instant client basic */
|
107
|
-
"oraociei12.dll",
|
108
|
-
"oraociei11.dll",
|
109
|
-
"oraociei10.dll",
|
110
|
-
/* instant client basic lite */
|
111
|
-
"oraociicus12.dll",
|
112
|
-
"oraociicus11.dll",
|
113
|
-
"oraociicus10.dll",
|
114
|
-
NULL,
|
115
|
-
};
|
116
|
-
|
117
150
|
static hook_func_entry_t tcp_functions[] = {
|
118
151
|
{"WSARecv", (void*)hook_WSARecv, NULL},
|
152
|
+
{"setsockopt", (void*)hook_setsockopt, NULL},
|
119
153
|
{NULL, NULL, NULL},
|
120
154
|
};
|
121
155
|
|
@@ -123,22 +157,112 @@ static hook_func_entry_t tcp_functions[] = {
|
|
123
157
|
static int WSAAPI hook_WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
124
158
|
{
|
125
159
|
socket_entry_t entry;
|
160
|
+
int enable_cancel = oci8_cancel_read_at_exit;
|
126
161
|
int rv;
|
127
162
|
|
128
|
-
|
163
|
+
if (enable_cancel > 0) {
|
164
|
+
socket_entry_set(&entry, s);
|
165
|
+
}
|
129
166
|
rv = WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);
|
130
|
-
|
167
|
+
if (enable_cancel > 0) {
|
168
|
+
socket_entry_clear(&entry);
|
169
|
+
}
|
131
170
|
return rv;
|
132
171
|
}
|
133
172
|
|
173
|
+
static int is_target_dll(MODULEENTRY32 *me)
|
174
|
+
{
|
175
|
+
static const char *basenames[] = {
|
176
|
+
"orantcp", // ORACLE_HOME-based client
|
177
|
+
"oraociei", // instant client basic
|
178
|
+
"oraociicus", // instant client basic lite
|
179
|
+
NULL,
|
180
|
+
};
|
181
|
+
const char **basename = basenames;
|
182
|
+
while (*basename != NULL) {
|
183
|
+
if (strnicmp(me->szModule, *basename, strlen(*basename)) == 0) {
|
184
|
+
break;
|
185
|
+
}
|
186
|
+
basename++;
|
187
|
+
}
|
188
|
+
if (*basename == NULL) {
|
189
|
+
return 0;
|
190
|
+
}
|
191
|
+
// basename{zero_or_more_digits}.dll
|
192
|
+
const char *p = me->szModule + strlen(*basename);
|
193
|
+
while ('0' <= *p && *p <= '9') {
|
194
|
+
p++;
|
195
|
+
}
|
196
|
+
if (stricmp(p, ".dll") != 0) {
|
197
|
+
return 0;
|
198
|
+
}
|
199
|
+
if (GetProcAddress((HMODULE)me->modBaseAddr, "nttini") == NULL) {
|
200
|
+
return 0;
|
201
|
+
}
|
202
|
+
return 1;
|
203
|
+
}
|
204
|
+
|
134
205
|
void oci8_install_hook_functions()
|
135
206
|
{
|
207
|
+
static int hook_functions_installed = 0;
|
208
|
+
HKEY hKey;
|
209
|
+
DWORD type;
|
210
|
+
DWORD data;
|
211
|
+
DWORD cbData = sizeof(data);
|
212
|
+
const char *reg_key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
|
213
|
+
HANDLE hSnapshot;
|
214
|
+
MODULEENTRY32 me;
|
215
|
+
BOOL module_found = FALSE;
|
216
|
+
|
217
|
+
if (hook_functions_installed) {
|
218
|
+
return;
|
219
|
+
}
|
220
|
+
|
136
221
|
InitializeCriticalSectionAndSpinCount(&lock, 5000);
|
137
222
|
locK_is_initialized = 1;
|
138
223
|
|
139
|
-
|
224
|
+
#ifndef _MSC_VER
|
225
|
+
/* Get setsockopt in ws2_32.dll.
|
226
|
+
* setsockopt used by mingw compiler isn't same with that in ws2_32.dll.
|
227
|
+
*/
|
228
|
+
setsockopt = (setsockopt_t)GetProcAddress(GetModuleHandleA("WS2_32.DLL"), "setsockopt");
|
229
|
+
if (setsockopt == NULL){
|
230
|
+
rb_raise(rb_eRuntimeError, "setsockopt isn't found in WS2_32.DLL");
|
231
|
+
}
|
232
|
+
#endif
|
233
|
+
|
234
|
+
/* Get system-wide keepalive interval parameter.
|
235
|
+
* https://technet.microsoft.com/en-us/library/cc957548.aspx
|
236
|
+
*/
|
237
|
+
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, reg_key, 0, KEY_QUERY_VALUE, &hKey) != 0) {
|
238
|
+
rb_raise(rb_eRuntimeError, "failed to open the registry key HKLM\\%s", reg_key);
|
239
|
+
}
|
240
|
+
keepalive_interval = 1000; /* default value when the following entry isn't found. */
|
241
|
+
if (RegQueryValueEx(hKey, "KeepAliveInterval", NULL, &type, (LPBYTE)&data, &cbData) == 0) {
|
242
|
+
if (type == REG_DWORD) {
|
243
|
+
keepalive_interval = data;
|
244
|
+
}
|
245
|
+
}
|
246
|
+
RegCloseKey(hKey);
|
247
|
+
|
248
|
+
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
|
249
|
+
me.dwSize = sizeof(me);
|
250
|
+
if (Module32First(hSnapshot, &me)) {
|
251
|
+
do {
|
252
|
+
if (is_target_dll(&me)) {
|
253
|
+
module_found = TRUE;
|
254
|
+
if (replace_functions(me.modBaseAddr, me.szExePath, tcp_functions) != 0) {
|
255
|
+
CloseHandle(hSnapshot);
|
256
|
+
rb_raise(rb_eRuntimeError, "Hook error: %s", hook_errmsg);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
} while (Module32Next(hSnapshot, &me));
|
260
|
+
}
|
261
|
+
CloseHandle(hSnapshot);
|
262
|
+
if (!module_found) {
|
140
263
|
rb_raise(rb_eRuntimeError, "No DLL is found to hook.");
|
141
264
|
}
|
265
|
+
hook_functions_installed = 1;
|
142
266
|
}
|
143
267
|
|
144
268
|
static void shutdown_socket(socket_entry_t *entry)
|
@@ -160,35 +284,89 @@ static ssize_t hook_read(int fd, void *buf, size_t count);
|
|
160
284
|
#define SO_EXT "so"
|
161
285
|
#endif
|
162
286
|
|
163
|
-
static const char * const files[] = {
|
164
|
-
"libclntsh." SO_EXT ".12.1",
|
165
|
-
"libclntsh." SO_EXT ".11.1",
|
166
|
-
"libclntsh." SO_EXT ".10.1",
|
167
|
-
"libclntsh." SO_EXT ".9.0",
|
168
|
-
NULL,
|
169
|
-
};
|
170
|
-
|
171
287
|
static hook_func_entry_t functions[] = {
|
172
288
|
{"read", (void*)hook_read, NULL},
|
289
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
290
|
+
{"setsockopt", (void*)hook_setsockopt, NULL},
|
291
|
+
#endif
|
173
292
|
{NULL, NULL, NULL},
|
174
293
|
};
|
175
294
|
|
176
295
|
static ssize_t hook_read(int fd, void *buf, size_t count)
|
177
296
|
{
|
178
297
|
socket_entry_t entry;
|
298
|
+
int enable_cancel = oci8_cancel_read_at_exit;
|
179
299
|
ssize_t rv;
|
180
300
|
|
181
|
-
|
301
|
+
if (enable_cancel > 0) {
|
302
|
+
socket_entry_set(&entry, fd);
|
303
|
+
}
|
182
304
|
rv = read(fd, buf, count);
|
183
|
-
|
305
|
+
if (enable_cancel > 0) {
|
306
|
+
socket_entry_clear(&entry);
|
307
|
+
}
|
184
308
|
return rv;
|
185
309
|
}
|
186
310
|
|
311
|
+
static void *ocifunc_addr(void *dlsym_handle, const char **file)
|
312
|
+
{
|
313
|
+
void *addr = dlsym(dlsym_handle, "OCIEnvCreate");
|
314
|
+
Dl_info dli;
|
315
|
+
|
316
|
+
if (addr == NULL) {
|
317
|
+
return NULL;
|
318
|
+
}
|
319
|
+
if (dladdr(addr, &dli) == 0) {
|
320
|
+
return NULL;
|
321
|
+
}
|
322
|
+
if (strstr(dli.dli_fname, "/libclntsh." SO_EXT) == NULL) {
|
323
|
+
return NULL;
|
324
|
+
}
|
325
|
+
*file = dli.dli_fname;
|
326
|
+
return addr;
|
327
|
+
}
|
328
|
+
|
329
|
+
#ifdef __linux__
|
330
|
+
#include <link.h>
|
331
|
+
static void *ocifunc_addr_linux(const char **file)
|
332
|
+
{
|
333
|
+
struct link_map *lm;
|
334
|
+
for (lm = _r_debug.r_map; lm != NULL; lm = lm->l_next) {
|
335
|
+
if (strstr(lm->l_name, "/libclntsh." SO_EXT) != NULL) {
|
336
|
+
*file = lm->l_name;
|
337
|
+
return (void*)lm->l_addr;
|
338
|
+
}
|
339
|
+
}
|
340
|
+
return NULL;
|
341
|
+
}
|
342
|
+
#endif
|
343
|
+
|
187
344
|
void oci8_install_hook_functions(void)
|
188
345
|
{
|
189
|
-
|
346
|
+
static int hook_functions_installed = 0;
|
347
|
+
void *addr;
|
348
|
+
const char *file;
|
349
|
+
|
350
|
+
if (hook_functions_installed) {
|
351
|
+
return;
|
352
|
+
}
|
353
|
+
addr = ocifunc_addr(RTLD_DEFAULT, &file);
|
354
|
+
if (addr == NULL) {
|
355
|
+
/* OCI symbols may be hooked by LD_PRELOAD. */
|
356
|
+
addr = ocifunc_addr(RTLD_NEXT, &file);
|
357
|
+
}
|
358
|
+
#ifdef __linux__
|
359
|
+
if (addr == NULL) {
|
360
|
+
addr = ocifunc_addr_linux(&file);
|
361
|
+
}
|
362
|
+
#endif
|
363
|
+
if (addr == NULL) {
|
190
364
|
rb_raise(rb_eRuntimeError, "No shared library is found to hook.");
|
191
365
|
}
|
366
|
+
if (replace_functions(addr, file, functions) != 0) {
|
367
|
+
rb_raise(rb_eRuntimeError, "Hook error: %s", hook_errmsg);
|
368
|
+
}
|
369
|
+
hook_functions_installed = 1;
|
192
370
|
}
|
193
371
|
|
194
372
|
static void shutdown_socket(socket_entry_t *entry)
|
@@ -197,11 +375,46 @@ static void shutdown_socket(socket_entry_t *entry)
|
|
197
375
|
}
|
198
376
|
#endif
|
199
377
|
|
378
|
+
#ifdef SUPPORT_TCP_KEEPALIVE_TIME
|
379
|
+
static int WSAAPI hook_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen)
|
380
|
+
{
|
381
|
+
int rv = setsockopt(sockfd, level, optname, optval, optlen);
|
382
|
+
|
383
|
+
if (rv == 0 && level == SOL_SOCKET && optname == SO_KEEPALIVE
|
384
|
+
&& optlen == sizeof(int) && *(const int*)optval != 0) {
|
385
|
+
/* If Oracle client libraries enables keepalive by (ENABLE=BROKEN),
|
386
|
+
* set per-connection keepalive socket options to overwrite
|
387
|
+
* system-wide setting.
|
388
|
+
*/
|
389
|
+
if (oci8_tcp_keepalive_time > 0) {
|
390
|
+
#if defined(USE_TCP_KEEPALIVE) /* macOS */
|
391
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, &oci8_tcp_keepalive_time, sizeof(int));
|
392
|
+
#elif defined(USE_TCP_KEEPALIVE_THRESHOLD) /* Solaris */
|
393
|
+
unsigned int millisec = oci8_tcp_keepalive_time * 1000;
|
394
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &millisec, sizeof(millisec));
|
395
|
+
#elif defined(USE_TCP_KEEPIDLE) /* Linux, etc */
|
396
|
+
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &oci8_tcp_keepalive_time, sizeof(int));
|
397
|
+
#elif defined(WINDOWS)
|
398
|
+
struct tcp_keepalive vals;
|
399
|
+
DWORD dummy;
|
400
|
+
|
401
|
+
vals.onoff = 1;
|
402
|
+
vals.keepalivetime = oci8_tcp_keepalive_time * 1000;
|
403
|
+
vals.keepaliveinterval = keepalive_interval;
|
404
|
+
WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0,
|
405
|
+
&dummy, NULL, NULL);
|
406
|
+
#endif
|
407
|
+
}
|
408
|
+
}
|
409
|
+
return rv;
|
410
|
+
}
|
411
|
+
#endif
|
412
|
+
|
200
413
|
void oci8_shutdown_sockets(void)
|
201
414
|
{
|
202
415
|
socket_entry_t *entry;
|
203
416
|
|
204
|
-
#ifdef
|
417
|
+
#ifdef WINDOWS
|
205
418
|
if (!locK_is_initialized) {
|
206
419
|
return;
|
207
420
|
}
|