curb 1.3.5 → 1.3.6
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 +4 -4
- data/README.md +57 -0
- data/Rakefile +8 -3
- data/doc.rb +48 -8
- data/ext/curb.c +24 -0
- data/ext/curb.h +3 -3
- data/ext/curb_easy.c +1378 -55
- data/ext/curb_easy.h +26 -0
- data/ext/curb_errors.c +2 -0
- data/ext/curb_errors.h +1 -0
- data/ext/curb_multi.c +48 -2
- data/ext/curb_multi.h +1 -0
- data/ext/extconf.rb +8 -0
- data/lib/curl/download.rb +160 -0
- data/lib/curl/easy.rb +113 -13
- data/lib/curl/multi.rb +172 -39
- data/lib/curl.rb +471 -11
- data/tests/bug_poison.rb +29 -0
- data/tests/tc_curl_download.rb +86 -0
- data/tests/tc_curl_easy.rb +76 -0
- data/tests/tc_curl_maxfilesize.rb +201 -1
- data/tests/tc_curl_multi.rb +258 -0
- data/tests/tc_curl_network_policy.rb +1475 -0
- data/tests/tc_curl_protocols.rb +351 -0
- data/tests/tc_fiber_scheduler.rb +41 -0
- metadata +7 -2
data/ext/curb_easy.c
CHANGED
|
@@ -11,16 +11,36 @@
|
|
|
11
11
|
#include "curb_multi.h"
|
|
12
12
|
|
|
13
13
|
#include <errno.h>
|
|
14
|
+
#include <stdlib.h>
|
|
14
15
|
#include <string.h>
|
|
15
16
|
#ifndef _WIN32
|
|
17
|
+
#include <sys/types.h>
|
|
18
|
+
#include <sys/socket.h>
|
|
19
|
+
#include <netinet/in.h>
|
|
20
|
+
#include <arpa/inet.h>
|
|
21
|
+
#else
|
|
22
|
+
#include <winsock2.h>
|
|
23
|
+
#include <ws2tcpip.h>
|
|
24
|
+
#endif
|
|
25
|
+
#ifndef _WIN32
|
|
16
26
|
#include <strings.h>
|
|
17
27
|
#endif
|
|
18
28
|
|
|
29
|
+
#if defined(HAVE_CURLOPT_OPENSOCKETFUNCTION) && defined(HAVE_CURLOPT_OPENSOCKETDATA)
|
|
30
|
+
#define CURB_HAVE_OPENSOCKET_NETWORK_POLICY 1
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
#if defined(HAVE_CURLOPT_PREREQFUNCTION) && defined(HAVE_CURLOPT_PREREQDATA)
|
|
34
|
+
#define CURB_HAVE_PREREQ_HOST_POLICY 1
|
|
35
|
+
#endif
|
|
36
|
+
|
|
19
37
|
extern VALUE mCurl;
|
|
20
38
|
|
|
21
39
|
static VALUE idCall;
|
|
22
40
|
static VALUE idJoin;
|
|
23
41
|
static VALUE rbstrAmp;
|
|
42
|
+
static ID idNetworkPolicyNone;
|
|
43
|
+
static ID idNetworkPolicyPublic;
|
|
24
44
|
|
|
25
45
|
#ifdef RDOC_NEVER_DEFINED
|
|
26
46
|
mCurl = rb_define_module("Curl");
|
|
@@ -44,9 +64,649 @@ static FILE * rb_io_stdio_file(rb_io_t *fptr) {
|
|
|
44
64
|
#endif
|
|
45
65
|
static struct curl_slist *duplicate_curl_slist(struct curl_slist *list);
|
|
46
66
|
static size_t proc_data_handler(char *stream, size_t size, size_t nmemb, VALUE proc);
|
|
67
|
+
static int curb_array_includes_string(VALUE list, VALUE value);
|
|
47
68
|
|
|
48
69
|
/* ================== CURL HANDLER FUNCS ==============*/
|
|
49
70
|
|
|
71
|
+
static int curb_ipv4_is_unsafe_destination(const unsigned char *ip) {
|
|
72
|
+
if (ip[0] == 0) return 1; /* 0.0.0.0/8 */
|
|
73
|
+
if (ip[0] == 10) return 1; /* RFC1918 */
|
|
74
|
+
if (ip[0] == 100 && (ip[1] & 0xc0) == 0x40) return 1; /* 100.64.0.0/10 */
|
|
75
|
+
if (ip[0] == 100 && ip[1] == 100 && ip[2] == 100 && ip[3] == 200) return 1;
|
|
76
|
+
if (ip[0] == 127) return 1; /* loopback */
|
|
77
|
+
if (ip[0] == 169 && ip[1] == 254) return 1; /* link-local / metadata */
|
|
78
|
+
if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) return 1;/* RFC1918 */
|
|
79
|
+
if (ip[0] == 192 && ip[1] == 168) return 1; /* RFC1918 */
|
|
80
|
+
if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0) return 1; /* IETF protocol assignments */
|
|
81
|
+
if (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) return 1; /* TEST-NET-1 */
|
|
82
|
+
if (ip[0] == 198 && (ip[1] == 18 || ip[1] == 19)) return 1;
|
|
83
|
+
if (ip[0] == 198 && ip[1] == 51 && ip[2] == 100) return 1;
|
|
84
|
+
if (ip[0] == 203 && ip[1] == 0 && ip[2] == 113) return 1;
|
|
85
|
+
if (ip[0] >= 224) return 1; /* multicast/reserved */
|
|
86
|
+
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static int curb_ip_prefix_matches(const unsigned char *ip, const unsigned char *prefix, int bits) {
|
|
91
|
+
int full_bytes = bits / 8;
|
|
92
|
+
int remaining_bits = bits % 8;
|
|
93
|
+
int i;
|
|
94
|
+
|
|
95
|
+
for (i = 0; i < full_bytes; i++) {
|
|
96
|
+
if (ip[i] != prefix[i]) return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (remaining_bits) {
|
|
100
|
+
unsigned char mask = (unsigned char)(0xff << (8 - remaining_bits));
|
|
101
|
+
if ((ip[full_bytes] & mask) != (prefix[full_bytes] & mask)) return 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static int curb_ipv6_prefix_matches(const unsigned char *ip, const unsigned char *prefix, int bits) {
|
|
108
|
+
return curb_ip_prefix_matches(ip, prefix, bits);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static int curb_ipv6_is_all_zero(const unsigned char *ip) {
|
|
112
|
+
int i;
|
|
113
|
+
|
|
114
|
+
for (i = 0; i < 16; i++) {
|
|
115
|
+
if (ip[i] != 0) return 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static int curb_ipv6_is_ipv4_mapped(const unsigned char *ip) {
|
|
122
|
+
static const unsigned char prefix[12] = {
|
|
123
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return curb_ipv6_prefix_matches(ip, prefix, 96);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static int curb_ipv6_is_ipv4_compatible(const unsigned char *ip) {
|
|
130
|
+
static const unsigned char prefix[12] = {
|
|
131
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return curb_ipv6_prefix_matches(ip, prefix, 96);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static int curb_ipv6_is_nat64_well_known(const unsigned char *ip) {
|
|
138
|
+
static const unsigned char prefix[12] = {
|
|
139
|
+
0x00, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return curb_ipv6_prefix_matches(ip, prefix, 96);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static int curb_ipv6_is_nat64_local_use(const unsigned char *ip) {
|
|
146
|
+
static const unsigned char prefix[6] = {
|
|
147
|
+
0x00, 0x64, 0xff, 0x9b, 0x00, 0x01
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
return curb_ipv6_prefix_matches(ip, prefix, 48);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static int curb_ipv6_is_unsafe_destination(const unsigned char *ip) {
|
|
154
|
+
static const unsigned char documentation_prefix[4] = { 0x20, 0x01, 0x0d, 0xb8 };
|
|
155
|
+
static const unsigned char benchmarking_prefix[6] = { 0x20, 0x01, 0x00, 0x02, 0, 0 };
|
|
156
|
+
|
|
157
|
+
if (curb_ipv6_is_all_zero(ip)) return 1; /* ::/128 */
|
|
158
|
+
if (curb_ipv6_is_ipv4_mapped(ip)) return curb_ipv4_is_unsafe_destination(ip + 12);
|
|
159
|
+
if (curb_ipv6_is_nat64_well_known(ip)) return curb_ipv4_is_unsafe_destination(ip + 12);
|
|
160
|
+
if (curb_ipv6_is_nat64_local_use(ip)) return 1;
|
|
161
|
+
if (curb_ipv6_is_ipv4_compatible(ip)) return 1; /* deprecated non-public space */
|
|
162
|
+
if (ip[0] == 0 && ip[1] == 0 && ip[14] == 0 && ip[15] == 1) return 1;
|
|
163
|
+
if ((ip[0] & 0xfe) == 0xfc) return 1; /* fc00::/7 */
|
|
164
|
+
if (ip[0] == 0xfe && (ip[1] & 0xc0) == 0x80) return 1; /* fe80::/10 */
|
|
165
|
+
if (ip[0] == 0xfe && (ip[1] & 0xc0) == 0xc0) return 1; /* fec0::/10 */
|
|
166
|
+
if (ip[0] == 0xff) return 1; /* multicast */
|
|
167
|
+
if (curb_ipv6_prefix_matches(ip, documentation_prefix, 32)) return 1;
|
|
168
|
+
if (curb_ipv6_prefix_matches(ip, benchmarking_prefix, 48)) return 1;
|
|
169
|
+
if (ip[0] == 0x20 && ip[1] == 0x02) return 1; /* 2002::/16 */
|
|
170
|
+
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static void curb_clear_network_allowed_cidr_rules(ruby_curl_easy *rbce) {
|
|
175
|
+
if (!rbce || !rbce->network_allowed_cidr_rules) return;
|
|
176
|
+
|
|
177
|
+
xfree(rbce->network_allowed_cidr_rules);
|
|
178
|
+
rbce->network_allowed_cidr_rules = NULL;
|
|
179
|
+
rbce->network_allowed_cidr_rule_count = 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static void curb_raise_invalid_cidr(char *tmp, const char *cidr) {
|
|
183
|
+
if (tmp) xfree(tmp);
|
|
184
|
+
rb_raise(rb_eArgError, "invalid CIDR range: %s", cidr);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
static void curb_parse_cidr_rule(const char *cidr, curb_cidr_rule *rule) {
|
|
188
|
+
size_t len;
|
|
189
|
+
char *tmp;
|
|
190
|
+
char *address;
|
|
191
|
+
char *prefix_str = NULL;
|
|
192
|
+
char *slash;
|
|
193
|
+
long prefix = -1;
|
|
194
|
+
int max_prefix = 0;
|
|
195
|
+
unsigned char parsed[16];
|
|
196
|
+
|
|
197
|
+
if (!cidr || !rule) {
|
|
198
|
+
rb_raise(rb_eArgError, "invalid CIDR range");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
len = strlen(cidr);
|
|
202
|
+
if (len == 0) {
|
|
203
|
+
rb_raise(rb_eArgError, "invalid CIDR range: %s", cidr);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
tmp = ALLOC_N(char, len + 1);
|
|
207
|
+
memcpy(tmp, cidr, len + 1);
|
|
208
|
+
address = tmp;
|
|
209
|
+
|
|
210
|
+
if (address[0] == '[') {
|
|
211
|
+
char *closing = strchr(address, ']');
|
|
212
|
+
if (!closing) curb_raise_invalid_cidr(tmp, cidr);
|
|
213
|
+
|
|
214
|
+
*closing = '\0';
|
|
215
|
+
address++;
|
|
216
|
+
|
|
217
|
+
if (closing[1] == '/') {
|
|
218
|
+
prefix_str = closing + 2;
|
|
219
|
+
} else if (closing[1] != '\0') {
|
|
220
|
+
curb_raise_invalid_cidr(tmp, cidr);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
slash = strchr(address, '/');
|
|
224
|
+
if (slash) {
|
|
225
|
+
*slash = '\0';
|
|
226
|
+
prefix_str = slash + 1;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
memset(rule, 0, sizeof(curb_cidr_rule));
|
|
231
|
+
|
|
232
|
+
if (inet_pton(AF_INET, address, parsed) == 1) {
|
|
233
|
+
rule->family = CURB_CIDR_FAMILY_IPV4;
|
|
234
|
+
max_prefix = 32;
|
|
235
|
+
memcpy(rule->address, parsed, 4);
|
|
236
|
+
} else if (inet_pton(AF_INET6, address, parsed) == 1) {
|
|
237
|
+
rule->family = CURB_CIDR_FAMILY_IPV6;
|
|
238
|
+
max_prefix = 128;
|
|
239
|
+
memcpy(rule->address, parsed, 16);
|
|
240
|
+
} else {
|
|
241
|
+
curb_raise_invalid_cidr(tmp, cidr);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (prefix_str) {
|
|
245
|
+
char *endptr = NULL;
|
|
246
|
+
|
|
247
|
+
if (prefix_str[0] == '\0') curb_raise_invalid_cidr(tmp, cidr);
|
|
248
|
+
|
|
249
|
+
errno = 0;
|
|
250
|
+
prefix = strtol(prefix_str, &endptr, 10);
|
|
251
|
+
if (errno != 0 || !endptr || *endptr != '\0' || prefix < 0 || prefix > max_prefix) {
|
|
252
|
+
curb_raise_invalid_cidr(tmp, cidr);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
prefix = max_prefix;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
rule->prefix_bits = (unsigned char)prefix;
|
|
259
|
+
xfree(tmp);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
static VALUE curb_normalize_cidr_list(VALUE cidrs) {
|
|
263
|
+
VALUE list;
|
|
264
|
+
VALUE normalized;
|
|
265
|
+
long i;
|
|
266
|
+
|
|
267
|
+
if (NIL_P(cidrs)) return Qnil;
|
|
268
|
+
|
|
269
|
+
list = rb_check_array_type(cidrs);
|
|
270
|
+
if (NIL_P(list)) {
|
|
271
|
+
list = rb_ary_new_from_args(1, cidrs);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
normalized = rb_ary_new_capa(RARRAY_LEN(list));
|
|
275
|
+
for (i = 0; i < RARRAY_LEN(list); i++) {
|
|
276
|
+
VALUE item = rb_ary_entry(list, i);
|
|
277
|
+
VALUE cidr = rb_obj_as_string(item);
|
|
278
|
+
curb_cidr_rule unused_rule;
|
|
279
|
+
|
|
280
|
+
curb_parse_cidr_rule(StringValueCStr(cidr), &unused_rule);
|
|
281
|
+
if (!curb_array_includes_string(normalized, cidr)) {
|
|
282
|
+
rb_ary_push(normalized, rb_str_dup(cidr));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return normalized;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static VALUE curb_dup_string_array(VALUE list) {
|
|
290
|
+
VALUE copy;
|
|
291
|
+
long i;
|
|
292
|
+
|
|
293
|
+
if (NIL_P(list)) return Qnil;
|
|
294
|
+
|
|
295
|
+
copy = rb_ary_new_capa(RARRAY_LEN(list));
|
|
296
|
+
for (i = 0; i < RARRAY_LEN(list); i++) {
|
|
297
|
+
rb_ary_push(copy, rb_str_dup(rb_ary_entry(list, i)));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return copy;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
static int curb_array_includes_string(VALUE list, VALUE value) {
|
|
304
|
+
long i;
|
|
305
|
+
|
|
306
|
+
for (i = 0; i < RARRAY_LEN(list); i++) {
|
|
307
|
+
if (rb_str_cmp(rb_ary_entry(list, i), value) == 0) return 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
static char curb_ascii_downcase(char c) {
|
|
314
|
+
if (c >= 'A' && c <= 'Z') return (char)(c - 'A' + 'a');
|
|
315
|
+
return c;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
static char *curb_normalized_host_from_range(const char *start, const char *end, int raise_errors) {
|
|
319
|
+
char *host;
|
|
320
|
+
size_t len;
|
|
321
|
+
size_t i;
|
|
322
|
+
|
|
323
|
+
while (start < end && (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n')) start++;
|
|
324
|
+
while (end > start && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) end--;
|
|
325
|
+
while (end > start && end[-1] == '.') end--;
|
|
326
|
+
|
|
327
|
+
if (end <= start) {
|
|
328
|
+
if (!raise_errors) return NULL;
|
|
329
|
+
rb_raise(rb_eArgError, "allowed_hosts cannot include blank entries");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
len = (size_t)(end - start);
|
|
333
|
+
host = ALLOC_N(char, len + 1);
|
|
334
|
+
for (i = 0; i < len; i++) {
|
|
335
|
+
host[i] = curb_ascii_downcase(start[i]);
|
|
336
|
+
}
|
|
337
|
+
host[len] = '\0';
|
|
338
|
+
|
|
339
|
+
return host;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
static char *curb_normalize_host_value_impl(const char *value, int raise_errors) {
|
|
343
|
+
const char *start;
|
|
344
|
+
const char *end;
|
|
345
|
+
const char *scheme;
|
|
346
|
+
const char *authority_end;
|
|
347
|
+
const char *scan;
|
|
348
|
+
const char *last_at = NULL;
|
|
349
|
+
int colon_count = 0;
|
|
350
|
+
|
|
351
|
+
if (!value) {
|
|
352
|
+
if (!raise_errors) return NULL;
|
|
353
|
+
rb_raise(rb_eArgError, "allowed_hosts cannot include blank entries");
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
start = value;
|
|
357
|
+
end = value + strlen(value);
|
|
358
|
+
while (start < end && (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n')) start++;
|
|
359
|
+
while (end > start && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) end--;
|
|
360
|
+
if (end <= start) {
|
|
361
|
+
if (!raise_errors) return NULL;
|
|
362
|
+
rb_raise(rb_eArgError, "allowed_hosts cannot include blank entries");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
scheme = strstr(start, "://");
|
|
366
|
+
if (scheme && scheme < end) {
|
|
367
|
+
start = scheme + 3;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
authority_end = end;
|
|
371
|
+
for (scan = start; scan < end; scan++) {
|
|
372
|
+
if (*scan == '/' || *scan == '?' || *scan == '#') {
|
|
373
|
+
authority_end = scan;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (scan = start; scan < authority_end; scan++) {
|
|
379
|
+
if (*scan == '@') last_at = scan;
|
|
380
|
+
}
|
|
381
|
+
if (last_at) start = last_at + 1;
|
|
382
|
+
|
|
383
|
+
if (start < authority_end && *start == '[') {
|
|
384
|
+
const char *closing = start + 1;
|
|
385
|
+
while (closing < authority_end && *closing != ']') closing++;
|
|
386
|
+
if (closing >= authority_end) {
|
|
387
|
+
if (!raise_errors) return NULL;
|
|
388
|
+
rb_raise(rb_eArgError, "invalid allowed host: %s", value);
|
|
389
|
+
}
|
|
390
|
+
return curb_normalized_host_from_range(start + 1, closing, raise_errors);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
for (scan = start; scan < authority_end; scan++) {
|
|
394
|
+
if (*scan == ':') colon_count++;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (colon_count <= 1) {
|
|
398
|
+
for (scan = start; scan < authority_end; scan++) {
|
|
399
|
+
if (*scan == ':') {
|
|
400
|
+
authority_end = scan;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return curb_normalized_host_from_range(start, authority_end, raise_errors);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
static char *curb_normalize_host_value(const char *value) {
|
|
410
|
+
return curb_normalize_host_value_impl(value, 1);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
static char *curb_try_normalize_host_value(const char *value) {
|
|
414
|
+
return curb_normalize_host_value_impl(value, 0);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
static VALUE curb_normalize_host_list(VALUE hosts) {
|
|
418
|
+
VALUE list;
|
|
419
|
+
VALUE normalized;
|
|
420
|
+
long i;
|
|
421
|
+
|
|
422
|
+
if (NIL_P(hosts)) return Qnil;
|
|
423
|
+
|
|
424
|
+
list = rb_check_array_type(hosts);
|
|
425
|
+
if (NIL_P(list)) {
|
|
426
|
+
list = rb_ary_new_from_args(1, hosts);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
normalized = rb_ary_new_capa(RARRAY_LEN(list));
|
|
430
|
+
for (i = 0; i < RARRAY_LEN(list); i++) {
|
|
431
|
+
VALUE item = rb_ary_entry(list, i);
|
|
432
|
+
VALUE host_value = rb_obj_as_string(item);
|
|
433
|
+
char *host = curb_normalize_host_value(StringValueCStr(host_value));
|
|
434
|
+
VALUE normalized_host = rb_str_new_cstr(host);
|
|
435
|
+
|
|
436
|
+
if (!curb_array_includes_string(normalized, normalized_host)) {
|
|
437
|
+
rb_ary_push(normalized, normalized_host);
|
|
438
|
+
}
|
|
439
|
+
xfree(host);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return normalized;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static void curb_clear_network_allowed_hosts(ruby_curl_easy *rbce) {
|
|
446
|
+
size_t i;
|
|
447
|
+
|
|
448
|
+
if (!rbce || !rbce->network_allowed_hosts) return;
|
|
449
|
+
|
|
450
|
+
for (i = 0; i < rbce->network_allowed_host_count; i++) {
|
|
451
|
+
if (rbce->network_allowed_hosts[i]) xfree(rbce->network_allowed_hosts[i]);
|
|
452
|
+
}
|
|
453
|
+
xfree(rbce->network_allowed_hosts);
|
|
454
|
+
rbce->network_allowed_hosts = NULL;
|
|
455
|
+
rbce->network_allowed_host_count = 0;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
static char *curb_strdup_cstr(const char *value) {
|
|
459
|
+
size_t len = strlen(value);
|
|
460
|
+
char *copy = ALLOC_N(char, len + 1);
|
|
461
|
+
memcpy(copy, value, len + 1);
|
|
462
|
+
return copy;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
static void curb_prepare_network_allowed_hosts(ruby_curl_easy *rbce) {
|
|
466
|
+
VALUE hosts;
|
|
467
|
+
long count;
|
|
468
|
+
long i;
|
|
469
|
+
char **rules;
|
|
470
|
+
|
|
471
|
+
if (!rbce) return;
|
|
472
|
+
|
|
473
|
+
curb_clear_network_allowed_hosts(rbce);
|
|
474
|
+
|
|
475
|
+
hosts = rb_hash_aref(rbce->opts, rb_easy_hkey("allowed_hosts"));
|
|
476
|
+
if (NIL_P(hosts)) return;
|
|
477
|
+
|
|
478
|
+
count = RARRAY_LEN(hosts);
|
|
479
|
+
if (count <= 0) return;
|
|
480
|
+
|
|
481
|
+
rules = ALLOC_N(char *, count);
|
|
482
|
+
for (i = 0; i < count; i++) {
|
|
483
|
+
VALUE host = rb_ary_entry(hosts, i);
|
|
484
|
+
rules[i] = curb_strdup_cstr(StringValueCStr(host));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
rbce->network_allowed_hosts = rules;
|
|
488
|
+
rbce->network_allowed_host_count = (size_t)count;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
static int curb_host_rules_match(const ruby_curl_easy *rbce, const char *host) {
|
|
492
|
+
size_t i;
|
|
493
|
+
|
|
494
|
+
if (!rbce || rbce->network_allowed_host_count == 0) return 1;
|
|
495
|
+
|
|
496
|
+
for (i = 0; i < rbce->network_allowed_host_count; i++) {
|
|
497
|
+
if (strcmp(host, rbce->network_allowed_hosts[i]) == 0) return 1;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return 0;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
static void curb_prepare_network_allowed_cidr_rules(ruby_curl_easy *rbce) {
|
|
504
|
+
VALUE cidrs;
|
|
505
|
+
long count;
|
|
506
|
+
long i;
|
|
507
|
+
curb_cidr_rule *rules;
|
|
508
|
+
|
|
509
|
+
if (!rbce) return;
|
|
510
|
+
|
|
511
|
+
curb_clear_network_allowed_cidr_rules(rbce);
|
|
512
|
+
|
|
513
|
+
cidrs = rb_hash_aref(rbce->opts, rb_easy_hkey("allowed_cidrs"));
|
|
514
|
+
if (NIL_P(cidrs)) return;
|
|
515
|
+
|
|
516
|
+
count = RARRAY_LEN(cidrs);
|
|
517
|
+
if (count <= 0) return;
|
|
518
|
+
|
|
519
|
+
rules = ALLOC_N(curb_cidr_rule, count);
|
|
520
|
+
for (i = 0; i < count; i++) {
|
|
521
|
+
VALUE cidr = rb_ary_entry(cidrs, i);
|
|
522
|
+
curb_parse_cidr_rule(StringValueCStr(cidr), &rules[i]);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
rbce->network_allowed_cidr_rules = rules;
|
|
526
|
+
rbce->network_allowed_cidr_rule_count = (size_t)count;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
static int curb_cidr_rules_match(const ruby_curl_easy *rbce, unsigned char family, const unsigned char *ip) {
|
|
530
|
+
size_t i;
|
|
531
|
+
|
|
532
|
+
if (!rbce || rbce->network_allowed_cidr_rule_count == 0) return 1;
|
|
533
|
+
|
|
534
|
+
for (i = 0; i < rbce->network_allowed_cidr_rule_count; i++) {
|
|
535
|
+
const curb_cidr_rule *rule = &rbce->network_allowed_cidr_rules[i];
|
|
536
|
+
if (rule->family != family) continue;
|
|
537
|
+
if (curb_ip_prefix_matches(ip, rule->address, rule->prefix_bits)) return 1;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return 0;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
static void curb_format_ipv4(char *buf, size_t len, const unsigned char *ip) {
|
|
544
|
+
snprintf(buf, len, "%u.%u.%u.%u",
|
|
545
|
+
(unsigned int)ip[0], (unsigned int)ip[1],
|
|
546
|
+
(unsigned int)ip[2], (unsigned int)ip[3]);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
static void curb_format_ipv6(char *buf, size_t len, const unsigned char *ip) {
|
|
550
|
+
#ifdef AF_INET6
|
|
551
|
+
#ifdef _WIN32
|
|
552
|
+
if (inet_ntop(AF_INET6, (void *)ip, buf, len)) return;
|
|
553
|
+
#else
|
|
554
|
+
if (inet_ntop(AF_INET6, (const void *)ip, buf, (socklen_t)len)) return;
|
|
555
|
+
#endif
|
|
556
|
+
#endif
|
|
557
|
+
|
|
558
|
+
snprintf(buf, len,
|
|
559
|
+
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
|
560
|
+
(unsigned int)ip[0], (unsigned int)ip[1],
|
|
561
|
+
(unsigned int)ip[2], (unsigned int)ip[3],
|
|
562
|
+
(unsigned int)ip[4], (unsigned int)ip[5],
|
|
563
|
+
(unsigned int)ip[6], (unsigned int)ip[7],
|
|
564
|
+
(unsigned int)ip[8], (unsigned int)ip[9],
|
|
565
|
+
(unsigned int)ip[10], (unsigned int)ip[11],
|
|
566
|
+
(unsigned int)ip[12], (unsigned int)ip[13],
|
|
567
|
+
(unsigned int)ip[14], (unsigned int)ip[15]);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
static void curb_store_destination_error(ruby_curl_easy *rbce, const char *address, const char *reason) {
|
|
571
|
+
if (!rbce) return;
|
|
572
|
+
|
|
573
|
+
rbce->unsafe_destination_blocked = 1;
|
|
574
|
+
|
|
575
|
+
if (reason && reason[0]) {
|
|
576
|
+
snprintf(rbce->unsafe_destination_error, CURL_ERROR_SIZE,
|
|
577
|
+
"blocked destination address %s %s by public network policy", address, reason);
|
|
578
|
+
} else {
|
|
579
|
+
snprintf(rbce->unsafe_destination_error, CURL_ERROR_SIZE,
|
|
580
|
+
"blocked unsafe destination address %s by public network policy", address);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
snprintf(rbce->err_buf, CURL_ERROR_SIZE, "%s", rbce->unsafe_destination_error);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
static void curb_store_unsafe_destination_error(ruby_curl_easy *rbce, const char *address) {
|
|
587
|
+
curb_store_destination_error(rbce, address, NULL);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
static void curb_store_host_allowlist_error(ruby_curl_easy *rbce, const char *host) {
|
|
591
|
+
if (!rbce) return;
|
|
592
|
+
|
|
593
|
+
rbce->unsafe_destination_blocked = 1;
|
|
594
|
+
snprintf(rbce->unsafe_destination_error, CURL_ERROR_SIZE,
|
|
595
|
+
"blocked URL host %s by safe mode host allowlist", host ? host : "unknown");
|
|
596
|
+
snprintf(rbce->err_buf, CURL_ERROR_SIZE, "%s", rbce->unsafe_destination_error);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
#ifdef CURB_HAVE_PREREQ_HOST_POLICY
|
|
600
|
+
static int curb_host_allowlist_prereq(void *clientp,
|
|
601
|
+
char *conn_primary_ip,
|
|
602
|
+
char *conn_local_ip,
|
|
603
|
+
int conn_primary_port,
|
|
604
|
+
int conn_local_port) {
|
|
605
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)clientp;
|
|
606
|
+
char *effective_url = NULL;
|
|
607
|
+
char *host = NULL;
|
|
608
|
+
CURLcode rc;
|
|
609
|
+
|
|
610
|
+
(void)conn_primary_ip;
|
|
611
|
+
(void)conn_local_ip;
|
|
612
|
+
(void)conn_primary_port;
|
|
613
|
+
(void)conn_local_port;
|
|
614
|
+
|
|
615
|
+
if (!rbce || rbce->network_allowed_host_count == 0) {
|
|
616
|
+
return CURL_PREREQFUNC_OK;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
rc = curl_easy_getinfo(rbce->curl, CURLINFO_EFFECTIVE_URL, &effective_url);
|
|
620
|
+
if (rc != CURLE_OK || !effective_url) {
|
|
621
|
+
curb_store_host_allowlist_error(rbce, "unknown");
|
|
622
|
+
return CURL_PREREQFUNC_ABORT;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
host = curb_try_normalize_host_value(effective_url);
|
|
626
|
+
if (!host) {
|
|
627
|
+
curb_store_host_allowlist_error(rbce, "unknown");
|
|
628
|
+
return CURL_PREREQFUNC_ABORT;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (!curb_host_rules_match(rbce, host)) {
|
|
632
|
+
curb_store_host_allowlist_error(rbce, host);
|
|
633
|
+
xfree(host);
|
|
634
|
+
return CURL_PREREQFUNC_ABORT;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
xfree(host);
|
|
638
|
+
return CURL_PREREQFUNC_OK;
|
|
639
|
+
}
|
|
640
|
+
#endif
|
|
641
|
+
|
|
642
|
+
#ifdef CURB_HAVE_OPENSOCKET_NETWORK_POLICY
|
|
643
|
+
static curl_socket_t curb_public_network_opensocket(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) {
|
|
644
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)clientp;
|
|
645
|
+
curl_socket_t sockfd;
|
|
646
|
+
char address_string[80];
|
|
647
|
+
int checked_destination_address = 0;
|
|
648
|
+
|
|
649
|
+
(void)purpose;
|
|
650
|
+
|
|
651
|
+
if (!address) {
|
|
652
|
+
curb_store_unsafe_destination_error(rbce, "unknown");
|
|
653
|
+
return CURL_SOCKET_BAD;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
#ifdef AF_INET
|
|
657
|
+
if (address->family == AF_INET && address->addrlen >= sizeof(struct sockaddr_in)) {
|
|
658
|
+
const struct sockaddr_in *sin = (const struct sockaddr_in *)&address->addr;
|
|
659
|
+
const unsigned char *ip = (const unsigned char *)&sin->sin_addr;
|
|
660
|
+
checked_destination_address = 1;
|
|
661
|
+
|
|
662
|
+
if (curb_ipv4_is_unsafe_destination(ip)) {
|
|
663
|
+
curb_format_ipv4(address_string, sizeof(address_string), ip);
|
|
664
|
+
curb_store_unsafe_destination_error(rbce, address_string);
|
|
665
|
+
return CURL_SOCKET_BAD;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
if (!curb_cidr_rules_match(rbce, CURB_CIDR_FAMILY_IPV4, ip)) {
|
|
669
|
+
curb_format_ipv4(address_string, sizeof(address_string), ip);
|
|
670
|
+
curb_store_destination_error(rbce, address_string, "outside allowed CIDR ranges");
|
|
671
|
+
return CURL_SOCKET_BAD;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
#endif
|
|
675
|
+
|
|
676
|
+
#ifdef AF_INET6
|
|
677
|
+
if (address->family == AF_INET6 && address->addrlen >= sizeof(struct sockaddr_in6)) {
|
|
678
|
+
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)&address->addr;
|
|
679
|
+
const unsigned char *ip = (const unsigned char *)&sin6->sin6_addr;
|
|
680
|
+
checked_destination_address = 1;
|
|
681
|
+
|
|
682
|
+
if (curb_ipv6_is_unsafe_destination(ip)) {
|
|
683
|
+
curb_format_ipv6(address_string, sizeof(address_string), ip);
|
|
684
|
+
curb_store_unsafe_destination_error(rbce, address_string);
|
|
685
|
+
return CURL_SOCKET_BAD;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (!curb_cidr_rules_match(rbce, CURB_CIDR_FAMILY_IPV6, ip)) {
|
|
689
|
+
curb_format_ipv6(address_string, sizeof(address_string), ip);
|
|
690
|
+
curb_store_destination_error(rbce, address_string, "outside allowed CIDR ranges");
|
|
691
|
+
return CURL_SOCKET_BAD;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
#endif
|
|
695
|
+
|
|
696
|
+
if (!checked_destination_address && rbce && rbce->network_allowed_cidr_rule_count > 0) {
|
|
697
|
+
curb_store_destination_error(rbce, "unknown", "outside allowed CIDR ranges");
|
|
698
|
+
return CURL_SOCKET_BAD;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
sockfd = socket(address->family, address->socktype, address->protocol);
|
|
702
|
+
if (sockfd == CURL_SOCKET_BAD) {
|
|
703
|
+
return CURL_SOCKET_BAD;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
return sockfd;
|
|
707
|
+
}
|
|
708
|
+
#endif
|
|
709
|
+
|
|
50
710
|
static VALUE callback_exception(VALUE unused, VALUE exception) {
|
|
51
711
|
return Qfalse;
|
|
52
712
|
}
|
|
@@ -93,6 +753,20 @@ static VALUE with_easy_callback_active(ruby_curl_easy *rbce, VALUE (*func)(VALUE
|
|
|
93
753
|
return rb_ensure(func, arg, ensure_clear_easy_callback_active, (VALUE)rbce);
|
|
94
754
|
}
|
|
95
755
|
|
|
756
|
+
static void ruby_curl_easy_enter_native(ruby_curl_easy *rbce) {
|
|
757
|
+
if (rbce) {
|
|
758
|
+
rbce->native_active++;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
static VALUE ruby_curl_easy_leave_native(VALUE arg) {
|
|
763
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)arg;
|
|
764
|
+
if (rbce && rbce->native_active > 0) {
|
|
765
|
+
rbce->native_active--;
|
|
766
|
+
}
|
|
767
|
+
return Qnil;
|
|
768
|
+
}
|
|
769
|
+
|
|
96
770
|
struct stream_read_call_args {
|
|
97
771
|
VALUE stream;
|
|
98
772
|
size_t read_bytes;
|
|
@@ -170,6 +844,30 @@ static int curl_seek_fail_result(void) {
|
|
|
170
844
|
#endif
|
|
171
845
|
}
|
|
172
846
|
|
|
847
|
+
static int ruby_curl_easy_body_limit_exceeded(ruby_curl_easy *rbce, size_t total) {
|
|
848
|
+
VALUE max_body_bytes = rb_easy_get("max_body_bytes");
|
|
849
|
+
curl_off_t limit;
|
|
850
|
+
|
|
851
|
+
if (NIL_P(max_body_bytes)) {
|
|
852
|
+
return 0;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
limit = (curl_off_t)NUM2LL(max_body_bytes);
|
|
856
|
+
if (limit <= 0) {
|
|
857
|
+
return 0;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
if ((curl_off_t)total > limit - rbce->downloaded_body_bytes) {
|
|
861
|
+
if (NIL_P(rbce->callback_error)) {
|
|
862
|
+
rbce->callback_error = rb_exc_new_cstr(eCurlErrFileSizeExceeded, "Maximum body size exceeded");
|
|
863
|
+
}
|
|
864
|
+
return 1;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
rbce->downloaded_body_bytes += (curl_off_t)total;
|
|
868
|
+
return 0;
|
|
869
|
+
}
|
|
870
|
+
|
|
173
871
|
/* Default body handler appends to easy.body_data buffer */
|
|
174
872
|
static size_t default_body_handler(char *stream,
|
|
175
873
|
size_t size,
|
|
@@ -178,6 +876,11 @@ static size_t default_body_handler(char *stream,
|
|
|
178
876
|
ruby_curl_easy *rbce = (ruby_curl_easy *)userdata;
|
|
179
877
|
size_t total = size * nmemb;
|
|
180
878
|
VALUE out = rb_easy_get("body_data");
|
|
879
|
+
|
|
880
|
+
if (ruby_curl_easy_body_limit_exceeded(rbce, total)) {
|
|
881
|
+
return 0;
|
|
882
|
+
}
|
|
883
|
+
|
|
181
884
|
if (NIL_P(out)) {
|
|
182
885
|
out = rb_easy_set("body_data", rb_str_buf_new(32768));
|
|
183
886
|
}
|
|
@@ -346,6 +1049,10 @@ static size_t proc_data_handler_body(char *stream,
|
|
|
346
1049
|
args.nmemb = nmemb;
|
|
347
1050
|
args.proc = rb_easy_get("body_proc");
|
|
348
1051
|
|
|
1052
|
+
if (ruby_curl_easy_body_limit_exceeded(rbce, size * nmemb)) {
|
|
1053
|
+
return 0;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
349
1056
|
dispatch_args.rbce = rbce;
|
|
350
1057
|
dispatch_args.func = call_proc_data_handler_wrapped;
|
|
351
1058
|
dispatch_args.arg = (VALUE)&args;
|
|
@@ -534,6 +1241,27 @@ static void ruby_curl_easy_clear_resolve_list(ruby_curl_easy *rbce) {
|
|
|
534
1241
|
rbce->curl_resolve = NULL;
|
|
535
1242
|
}
|
|
536
1243
|
|
|
1244
|
+
static void ruby_curl_easy_clear_connect_to_list(ruby_curl_easy *rbce) {
|
|
1245
|
+
if (!rbce || !rbce->curl_connect_to) {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
#ifdef HAVE_CURLOPT_CONNECT_TO
|
|
1249
|
+
if (rbce->curl) {
|
|
1250
|
+
curl_easy_setopt(rbce->curl, CURLOPT_CONNECT_TO, NULL);
|
|
1251
|
+
}
|
|
1252
|
+
#endif
|
|
1253
|
+
curl_slist_free_all(rbce->curl_connect_to);
|
|
1254
|
+
rbce->curl_connect_to = NULL;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
static void ruby_curl_easy_clear_setup_lists(ruby_curl_easy *rbce) {
|
|
1258
|
+
ruby_curl_easy_clear_headers_list(rbce);
|
|
1259
|
+
ruby_curl_easy_clear_proxy_headers_list(rbce);
|
|
1260
|
+
ruby_curl_easy_clear_ftp_commands_list(rbce);
|
|
1261
|
+
ruby_curl_easy_clear_resolve_list(rbce);
|
|
1262
|
+
ruby_curl_easy_clear_connect_to_list(rbce);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
537
1265
|
/* Legacy wrapper for external callers */
|
|
538
1266
|
void ruby_curl_easy_mark(ruby_curl_easy *rbce) {
|
|
539
1267
|
curl_easy_mark((void *)rbce);
|
|
@@ -590,6 +1318,9 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
|
|
|
590
1318
|
ruby_curl_easy_clear_proxy_headers_list(rbce);
|
|
591
1319
|
ruby_curl_easy_clear_ftp_commands_list(rbce);
|
|
592
1320
|
ruby_curl_easy_clear_resolve_list(rbce);
|
|
1321
|
+
ruby_curl_easy_clear_connect_to_list(rbce);
|
|
1322
|
+
curb_clear_network_allowed_cidr_rules(rbce);
|
|
1323
|
+
curb_clear_network_allowed_hosts(rbce);
|
|
593
1324
|
|
|
594
1325
|
if (rbce->curl) {
|
|
595
1326
|
/* disable any progress or debug events */
|
|
@@ -659,12 +1390,18 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
|
|
|
659
1390
|
rbce->opts = rb_hash_new();
|
|
660
1391
|
|
|
661
1392
|
memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
|
|
1393
|
+
memset(rbce->unsafe_destination_error, 0, CURL_ERROR_SIZE);
|
|
662
1394
|
|
|
663
1395
|
rbce->self = Qnil;
|
|
664
1396
|
rbce->curl_headers = NULL;
|
|
665
1397
|
rbce->curl_proxy_headers = NULL;
|
|
666
1398
|
rbce->curl_ftp_commands = NULL;
|
|
667
1399
|
rbce->curl_resolve = NULL;
|
|
1400
|
+
rbce->curl_connect_to = NULL;
|
|
1401
|
+
rbce->network_allowed_cidr_rules = NULL;
|
|
1402
|
+
rbce->network_allowed_hosts = NULL;
|
|
1403
|
+
rbce->network_allowed_cidr_rule_count = 0;
|
|
1404
|
+
rbce->network_allowed_host_count = 0;
|
|
668
1405
|
|
|
669
1406
|
/* various-typed opts */
|
|
670
1407
|
rbce->local_port = 0;
|
|
@@ -689,6 +1426,7 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
|
|
|
689
1426
|
rbce->ftp_filemethod = -1;
|
|
690
1427
|
rbce->http_version = CURL_HTTP_VERSION_NONE;
|
|
691
1428
|
rbce->resolve_mode = CURL_IPRESOLVE_WHATEVER;
|
|
1429
|
+
rbce->network_policy = CURB_NETWORK_POLICY_NONE;
|
|
692
1430
|
|
|
693
1431
|
/* bool opts */
|
|
694
1432
|
rbce->proxy_tunnel = 0;
|
|
@@ -705,6 +1443,12 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
|
|
|
705
1443
|
rbce->cookielist_engine_enabled = 0;
|
|
706
1444
|
rbce->ignore_content_length = 0;
|
|
707
1445
|
rbce->callback_active = 0;
|
|
1446
|
+
rbce->unsafe_destination_blocked = 0;
|
|
1447
|
+
rbce->allow_proxy = 0;
|
|
1448
|
+
rbce->allow_unix_socket = 0;
|
|
1449
|
+
rbce->forbid_reuse_set = 0;
|
|
1450
|
+
rbce->native_active = 0;
|
|
1451
|
+
rbce->forbid_reuse = 0;
|
|
708
1452
|
rbce->callback_error = Qnil;
|
|
709
1453
|
rbce->last_result = 0;
|
|
710
1454
|
}
|
|
@@ -837,10 +1581,18 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
|
|
|
837
1581
|
newrbce->curl_proxy_headers = (rbce->curl_proxy_headers) ? duplicate_curl_slist(rbce->curl_proxy_headers) : NULL;
|
|
838
1582
|
newrbce->curl_ftp_commands = (rbce->curl_ftp_commands) ? duplicate_curl_slist(rbce->curl_ftp_commands) : NULL;
|
|
839
1583
|
newrbce->curl_resolve = (rbce->curl_resolve) ? duplicate_curl_slist(rbce->curl_resolve) : NULL;
|
|
1584
|
+
newrbce->curl_connect_to = (rbce->curl_connect_to) ? duplicate_curl_slist(rbce->curl_connect_to) : NULL;
|
|
1585
|
+
newrbce->network_allowed_cidr_rules = NULL;
|
|
1586
|
+
newrbce->network_allowed_cidr_rule_count = 0;
|
|
1587
|
+
newrbce->network_allowed_hosts = NULL;
|
|
1588
|
+
newrbce->network_allowed_host_count = 0;
|
|
840
1589
|
|
|
841
1590
|
/* A cloned easy should not retain ownership reference to the original multi. */
|
|
842
1591
|
newrbce->multi = Qnil;
|
|
843
1592
|
newrbce->callback_error = Qnil;
|
|
1593
|
+
newrbce->unsafe_destination_blocked = 0;
|
|
1594
|
+
memset(newrbce->unsafe_destination_error, 0, CURL_ERROR_SIZE);
|
|
1595
|
+
newrbce->native_active = 0;
|
|
844
1596
|
|
|
845
1597
|
if (rbce->opts != Qnil) {
|
|
846
1598
|
newrbce->opts = rb_funcall(rbce->opts, rb_intern("dup"), 0);
|
|
@@ -889,6 +1641,10 @@ static VALUE ruby_curl_easy_close(VALUE self) {
|
|
|
889
1641
|
rb_raise(rb_eRuntimeError, "Cannot close an active curl handle within a callback");
|
|
890
1642
|
}
|
|
891
1643
|
|
|
1644
|
+
if (rbce->native_active) {
|
|
1645
|
+
rb_raise(rb_eRuntimeError, "Cannot close an active curl handle during native operation");
|
|
1646
|
+
}
|
|
1647
|
+
|
|
892
1648
|
ruby_curl_easy_free(rbce);
|
|
893
1649
|
|
|
894
1650
|
/* reinit the handle */
|
|
@@ -935,6 +1691,10 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
|
|
|
935
1691
|
rb_raise(rb_eRuntimeError, "Cannot close an active curl handle within a callback");
|
|
936
1692
|
}
|
|
937
1693
|
|
|
1694
|
+
if (rbce->native_active) {
|
|
1695
|
+
rb_raise(rb_eRuntimeError, "Cannot reset an active curl handle during native operation");
|
|
1696
|
+
}
|
|
1697
|
+
|
|
938
1698
|
opts_dup = rb_funcall(rbce->opts, rb_intern("dup"), 0);
|
|
939
1699
|
|
|
940
1700
|
ruby_curl_easy_cleanup(self, rbce);
|
|
@@ -1281,7 +2041,17 @@ static VALUE ruby_curl_easy_useragent_get(VALUE self) {
|
|
|
1281
2041
|
*
|
|
1282
2042
|
* This is handy if you want to perform a POST against a Curl::Multi instance.
|
|
1283
2043
|
*/
|
|
1284
|
-
|
|
2044
|
+
struct post_body_set_args {
|
|
2045
|
+
VALUE self;
|
|
2046
|
+
VALUE post_body;
|
|
2047
|
+
int force_http_get_on_nil;
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
static VALUE ruby_curl_easy_post_body_set_with_mode_body(VALUE argp) {
|
|
2051
|
+
struct post_body_set_args *args = (struct post_body_set_args *)argp;
|
|
2052
|
+
VALUE self = args->self;
|
|
2053
|
+
VALUE post_body = args->post_body;
|
|
2054
|
+
int force_http_get_on_nil = args->force_http_get_on_nil;
|
|
1285
2055
|
ruby_curl_easy *rbce;
|
|
1286
2056
|
CURL *curl;
|
|
1287
2057
|
|
|
@@ -1345,6 +2115,17 @@ static VALUE ruby_curl_easy_post_body_set_with_mode(VALUE self, VALUE post_body,
|
|
|
1345
2115
|
return Qnil;
|
|
1346
2116
|
}
|
|
1347
2117
|
|
|
2118
|
+
static VALUE ruby_curl_easy_post_body_set_with_mode(VALUE self, VALUE post_body, int force_http_get_on_nil) {
|
|
2119
|
+
ruby_curl_easy *rbce;
|
|
2120
|
+
struct post_body_set_args args = { self, post_body, force_http_get_on_nil };
|
|
2121
|
+
|
|
2122
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2123
|
+
ruby_curl_easy_enter_native(rbce);
|
|
2124
|
+
|
|
2125
|
+
return rb_ensure(ruby_curl_easy_post_body_set_with_mode_body, (VALUE)&args,
|
|
2126
|
+
ruby_curl_easy_leave_native, (VALUE)rbce);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
1348
2129
|
static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
|
|
1349
2130
|
return ruby_curl_easy_post_body_set_with_mode(self, post_body, 1);
|
|
1350
2131
|
}
|
|
@@ -1438,76 +2219,217 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
|
|
|
1438
2219
|
upload = ruby_curl_upload_new(cCurlUpload);
|
|
1439
2220
|
ruby_curl_upload_stream_set(upload, upload_stream);
|
|
1440
2221
|
|
|
1441
|
-
curl = rbce->curl;
|
|
1442
|
-
rb_easy_set("upload", upload); /* keep the upload object alive as long as
|
|
1443
|
-
the easy handle is active or until the upload
|
|
1444
|
-
is complete or terminated... */
|
|
2222
|
+
curl = rbce->curl;
|
|
2223
|
+
rb_easy_set("upload", upload); /* keep the upload object alive as long as
|
|
2224
|
+
the easy handle is active or until the upload
|
|
2225
|
+
is complete or terminated... */
|
|
2226
|
+
|
|
2227
|
+
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
|
|
2228
|
+
curl_easy_setopt(curl, CURLOPT_POST, 0);
|
|
2229
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
|
|
2230
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
|
|
2231
|
+
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
|
|
2232
|
+
curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
|
|
2233
|
+
#ifdef HAVE_CURLOPT_SEEKFUNCTION
|
|
2234
|
+
curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
|
|
2235
|
+
#endif
|
|
2236
|
+
curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
|
|
2237
|
+
#ifdef HAVE_CURLOPT_SEEKDATA
|
|
2238
|
+
curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
|
|
2239
|
+
#endif
|
|
2240
|
+
|
|
2241
|
+
if (!NIL_P(infile_size)) {
|
|
2242
|
+
curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(infile_size));
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
// if we made it this far, all should be well.
|
|
2246
|
+
return data;
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
/*
|
|
2250
|
+
* call-seq:
|
|
2251
|
+
* easy.ftp_commands = ["CWD /", "MKD directory"] => ["CWD /", ...]
|
|
2252
|
+
*
|
|
2253
|
+
* Explicitly sets the list of commands to execute on the FTP server when calling perform.
|
|
2254
|
+
*
|
|
2255
|
+
* NOTE:
|
|
2256
|
+
* - This maps to libcurl CURLOPT_QUOTE; it sends commands on the control connection.
|
|
2257
|
+
* - Do not include data-transfer commands like LIST/NLST/RETR/STOR here. libcurl does not
|
|
2258
|
+
* parse PASV/EPSV replies from QUOTE commands and will not establish the required data
|
|
2259
|
+
* connection. For directory listings, set CURLOPT_DIRLISTONLY (via `easy.set(:dirlistonly, true)`)
|
|
2260
|
+
* and request an FTP directory URL (e.g. "ftp://host/path/") so libcurl manages PASV/EPSV
|
|
2261
|
+
* and the data connection for you.
|
|
2262
|
+
*/
|
|
2263
|
+
static VALUE ruby_curl_easy_ftp_commands_set(VALUE self, VALUE ftp_commands) {
|
|
2264
|
+
CURB_OBJECT_HSETTER(ruby_curl_easy, ftp_commands);
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
/*
|
|
2268
|
+
* call-seq:
|
|
2269
|
+
* easy.ftp_commands => array or nil
|
|
2270
|
+
*/
|
|
2271
|
+
static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
|
|
2272
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_commands);
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
/*
|
|
2276
|
+
* call-seq:
|
|
2277
|
+
* easy.resolve = [ "example.com:80:127.0.0.1" ] => [ "example.com:80:127.0.0.1" ]
|
|
2278
|
+
*
|
|
2279
|
+
* Set the resolve list to statically resolve hostnames to IP addresses,
|
|
2280
|
+
* bypassing DNS for matching hostname/port combinations.
|
|
2281
|
+
*/
|
|
2282
|
+
static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
|
|
2283
|
+
CURB_OBJECT_HSETTER(ruby_curl_easy, resolve);
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
/*
|
|
2287
|
+
* call-seq:
|
|
2288
|
+
* easy.resolve => array or nil
|
|
2289
|
+
*/
|
|
2290
|
+
static VALUE ruby_curl_easy_resolve_get(VALUE self) {
|
|
2291
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, resolve);
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
/*
|
|
2295
|
+
* call-seq:
|
|
2296
|
+
* easy.connect_to = [ "example.com:80:127.0.0.1:80" ] => [ "example.com:80:127.0.0.1:80" ]
|
|
2297
|
+
*
|
|
2298
|
+
* Set the connect-to list to redirect matching request host/port pairs to
|
|
2299
|
+
* alternate connection host/port pairs.
|
|
2300
|
+
*/
|
|
2301
|
+
static VALUE ruby_curl_easy_connect_to_set(VALUE self, VALUE connect_to) {
|
|
2302
|
+
CURB_OBJECT_HSETTER(ruby_curl_easy, connect_to);
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
/*
|
|
2306
|
+
* call-seq:
|
|
2307
|
+
* easy.connect_to => array or nil
|
|
2308
|
+
*/
|
|
2309
|
+
static VALUE ruby_curl_easy_connect_to_get(VALUE self) {
|
|
2310
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, connect_to);
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
/*
|
|
2314
|
+
* call-seq:
|
|
2315
|
+
* easy.doh_url = "https://dns.example/dns-query" => "https://dns.example/dns-query"
|
|
2316
|
+
*
|
|
2317
|
+
* Set the DNS-over-HTTPS URL to use for resolving hostnames.
|
|
2318
|
+
*/
|
|
2319
|
+
static VALUE ruby_curl_easy_doh_url_set(VALUE self, VALUE doh_url) {
|
|
2320
|
+
CURB_OBJECT_HSETTER(ruby_curl_easy, doh_url);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
/*
|
|
2324
|
+
* call-seq:
|
|
2325
|
+
* easy.doh_url => string or nil
|
|
2326
|
+
*/
|
|
2327
|
+
static VALUE ruby_curl_easy_doh_url_get(VALUE self) {
|
|
2328
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, doh_url);
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
#ifdef HAVE_CURLOPT_DNS_SERVERS
|
|
2332
|
+
/*
|
|
2333
|
+
* call-seq:
|
|
2334
|
+
* easy.dns_servers => string or nil
|
|
2335
|
+
*/
|
|
2336
|
+
static VALUE ruby_curl_easy_dns_servers_get(VALUE self) {
|
|
2337
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, dns_servers);
|
|
2338
|
+
}
|
|
2339
|
+
#endif
|
|
2340
|
+
|
|
2341
|
+
static VALUE ruby_curl_easy_allow_unix_socket_set(VALUE self, VALUE allow) {
|
|
2342
|
+
ruby_curl_easy *rbce;
|
|
2343
|
+
|
|
2344
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2345
|
+
rbce->allow_unix_socket = RTEST(allow) ? 1 : 0;
|
|
1445
2346
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
|
|
1449
|
-
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
|
|
1450
|
-
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
|
|
1451
|
-
curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
|
|
1452
|
-
#ifdef HAVE_CURLOPT_SEEKFUNCTION
|
|
1453
|
-
curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
|
|
1454
|
-
#endif
|
|
1455
|
-
curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
|
|
1456
|
-
#ifdef HAVE_CURLOPT_SEEKDATA
|
|
1457
|
-
curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
|
|
1458
|
-
#endif
|
|
2347
|
+
return allow;
|
|
2348
|
+
}
|
|
1459
2349
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
2350
|
+
static VALUE ruby_curl_easy_allow_proxy_set(VALUE self, VALUE allow) {
|
|
2351
|
+
ruby_curl_easy *rbce;
|
|
1463
2352
|
|
|
1464
|
-
|
|
1465
|
-
|
|
2353
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2354
|
+
rbce->allow_proxy = RTEST(allow) ? 1 : 0;
|
|
2355
|
+
|
|
2356
|
+
return allow;
|
|
1466
2357
|
}
|
|
1467
2358
|
|
|
1468
2359
|
/*
|
|
1469
2360
|
* call-seq:
|
|
1470
|
-
* easy.
|
|
2361
|
+
* easy.allowed_cidrs = ["203.0.113.0/24"] => ["203.0.113.0/24"]
|
|
1471
2362
|
*
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
* - This maps to libcurl CURLOPT_QUOTE; it sends commands on the control connection.
|
|
1476
|
-
* - Do not include data-transfer commands like LIST/NLST/RETR/STOR here. libcurl does not
|
|
1477
|
-
* parse PASV/EPSV replies from QUOTE commands and will not establish the required data
|
|
1478
|
-
* connection. For directory listings, set CURLOPT_DIRLISTONLY (via `easy.set(:dirlistonly, true)`)
|
|
1479
|
-
* and request an FTP directory URL (e.g. "ftp://host/path/") so libcurl manages PASV/EPSV
|
|
1480
|
-
* and the data connection for you.
|
|
2363
|
+
* Set resolved-peer CIDR ranges that are allowed when network_policy is :public.
|
|
2364
|
+
* Private/local unsafe ranges are still blocked before this allowlist is
|
|
2365
|
+
* evaluated.
|
|
1481
2366
|
*/
|
|
1482
|
-
static VALUE
|
|
1483
|
-
|
|
2367
|
+
static VALUE ruby_curl_easy_allowed_cidrs_set(VALUE self, VALUE cidrs) {
|
|
2368
|
+
ruby_curl_easy *rbce;
|
|
2369
|
+
VALUE normalized;
|
|
2370
|
+
VALUE stored;
|
|
2371
|
+
|
|
2372
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2373
|
+
normalized = curb_normalize_cidr_list(cidrs);
|
|
2374
|
+
stored = curb_dup_string_array(normalized);
|
|
2375
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("allowed_cidrs"), stored);
|
|
2376
|
+
if (rbce->network_policy == CURB_NETWORK_POLICY_PUBLIC) {
|
|
2377
|
+
curb_prepare_network_allowed_cidr_rules(rbce);
|
|
2378
|
+
} else {
|
|
2379
|
+
curb_clear_network_allowed_cidr_rules(rbce);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
return curb_dup_string_array(stored);
|
|
1484
2383
|
}
|
|
1485
2384
|
|
|
1486
2385
|
/*
|
|
1487
2386
|
* call-seq:
|
|
1488
|
-
* easy.
|
|
2387
|
+
* easy.allowed_cidrs => array or nil
|
|
1489
2388
|
*/
|
|
1490
|
-
static VALUE
|
|
1491
|
-
|
|
2389
|
+
static VALUE ruby_curl_easy_allowed_cidrs_get(VALUE self) {
|
|
2390
|
+
ruby_curl_easy *rbce;
|
|
2391
|
+
VALUE cidrs;
|
|
2392
|
+
|
|
2393
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2394
|
+
cidrs = rb_hash_aref(rbce->opts, rb_easy_hkey("allowed_cidrs"));
|
|
2395
|
+
|
|
2396
|
+
return curb_dup_string_array(cidrs);
|
|
1492
2397
|
}
|
|
1493
2398
|
|
|
1494
2399
|
/*
|
|
1495
2400
|
* call-seq:
|
|
1496
|
-
* easy.
|
|
2401
|
+
* easy.allowed_hosts = ["api.example.com"] => ["api.example.com"]
|
|
1497
2402
|
*
|
|
1498
|
-
* Set
|
|
1499
|
-
*
|
|
2403
|
+
* Set URL hosts allowed for this handle. When libcurl supports
|
|
2404
|
+
* CURLOPT_PREREQFUNCTION, this is checked before each request, including
|
|
2405
|
+
* followed redirects.
|
|
1500
2406
|
*/
|
|
1501
|
-
static VALUE
|
|
1502
|
-
|
|
2407
|
+
static VALUE ruby_curl_easy_allowed_hosts_set(VALUE self, VALUE hosts) {
|
|
2408
|
+
ruby_curl_easy *rbce;
|
|
2409
|
+
VALUE normalized;
|
|
2410
|
+
VALUE stored;
|
|
2411
|
+
|
|
2412
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2413
|
+
normalized = curb_normalize_host_list(hosts);
|
|
2414
|
+
stored = curb_dup_string_array(normalized);
|
|
2415
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("allowed_hosts"), stored);
|
|
2416
|
+
curb_prepare_network_allowed_hosts(rbce);
|
|
2417
|
+
|
|
2418
|
+
return curb_dup_string_array(stored);
|
|
1503
2419
|
}
|
|
1504
2420
|
|
|
1505
2421
|
/*
|
|
1506
2422
|
* call-seq:
|
|
1507
|
-
* easy.
|
|
2423
|
+
* easy.allowed_hosts => array or nil
|
|
1508
2424
|
*/
|
|
1509
|
-
static VALUE
|
|
1510
|
-
|
|
2425
|
+
static VALUE ruby_curl_easy_allowed_hosts_get(VALUE self) {
|
|
2426
|
+
ruby_curl_easy *rbce;
|
|
2427
|
+
VALUE hosts;
|
|
2428
|
+
|
|
2429
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2430
|
+
hosts = rb_hash_aref(rbce->opts, rb_easy_hkey("allowed_hosts"));
|
|
2431
|
+
|
|
2432
|
+
return curb_dup_string_array(hosts);
|
|
1511
2433
|
}
|
|
1512
2434
|
|
|
1513
2435
|
/* ================== IMMED ATTRS ==================*/
|
|
@@ -2494,6 +3416,101 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
|
|
|
2494
3416
|
}
|
|
2495
3417
|
}
|
|
2496
3418
|
|
|
3419
|
+
/*
|
|
3420
|
+
* call-seq:
|
|
3421
|
+
* easy.network_policy => symbol
|
|
3422
|
+
*
|
|
3423
|
+
* Determine which native network policy will be enforced when libcurl opens
|
|
3424
|
+
* sockets for this handle.
|
|
3425
|
+
*/
|
|
3426
|
+
static VALUE ruby_curl_easy_network_policy_get(VALUE self) {
|
|
3427
|
+
ruby_curl_easy *rbce;
|
|
3428
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
3429
|
+
|
|
3430
|
+
switch (rbce->network_policy) {
|
|
3431
|
+
case CURB_NETWORK_POLICY_PUBLIC:
|
|
3432
|
+
return ID2SYM(idNetworkPolicyPublic);
|
|
3433
|
+
case CURB_NETWORK_POLICY_NONE:
|
|
3434
|
+
default:
|
|
3435
|
+
return ID2SYM(idNetworkPolicyNone);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
/*
|
|
3440
|
+
* call-seq:
|
|
3441
|
+
* easy.network_policy = symbol => symbol
|
|
3442
|
+
*
|
|
3443
|
+
* Supported values:
|
|
3444
|
+
* [:none] disables native destination checks.
|
|
3445
|
+
* [:public] blocks loopback, private, link-local, multicast,
|
|
3446
|
+
* unspecified, metadata, and other non-public addresses.
|
|
3447
|
+
*/
|
|
3448
|
+
static VALUE ruby_curl_easy_network_policy_set(VALUE self, VALUE network_policy) {
|
|
3449
|
+
ruby_curl_easy *rbce;
|
|
3450
|
+
ID network_policy_id;
|
|
3451
|
+
|
|
3452
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
3453
|
+
|
|
3454
|
+
if (NIL_P(network_policy)) {
|
|
3455
|
+
rbce->network_policy = CURB_NETWORK_POLICY_NONE;
|
|
3456
|
+
return ID2SYM(idNetworkPolicyNone);
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3459
|
+
if (TYPE(network_policy) != T_SYMBOL) {
|
|
3460
|
+
rb_raise(rb_eTypeError, "network_policy must be a Symbol");
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
network_policy_id = rb_to_id(network_policy);
|
|
3464
|
+
|
|
3465
|
+
if (network_policy_id == idNetworkPolicyNone) {
|
|
3466
|
+
rbce->network_policy = CURB_NETWORK_POLICY_NONE;
|
|
3467
|
+
rbce->allow_proxy = 0;
|
|
3468
|
+
return network_policy;
|
|
3469
|
+
} else if (network_policy_id == idNetworkPolicyPublic) {
|
|
3470
|
+
#ifndef CURB_HAVE_OPENSOCKET_NETWORK_POLICY
|
|
3471
|
+
rb_raise(rb_eNotImpError, "network_policy=:public requires CURLOPT_OPENSOCKETFUNCTION support");
|
|
3472
|
+
#else
|
|
3473
|
+
rbce->network_policy = CURB_NETWORK_POLICY_PUBLIC;
|
|
3474
|
+
rbce->allow_proxy = 0;
|
|
3475
|
+
return network_policy;
|
|
3476
|
+
#endif
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
rb_raise(rb_eArgError, "network_policy must be one of :none, :public");
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
/*
|
|
3483
|
+
* call-seq:
|
|
3484
|
+
* easy.unsafe_destination_error => string or nil
|
|
3485
|
+
*
|
|
3486
|
+
* Return the native network policy block reason from the most recent transfer.
|
|
3487
|
+
*/
|
|
3488
|
+
static VALUE ruby_curl_easy_unsafe_destination_error_get(VALUE self) {
|
|
3489
|
+
ruby_curl_easy *rbce;
|
|
3490
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
3491
|
+
|
|
3492
|
+
if (rbce->unsafe_destination_blocked && rbce->unsafe_destination_error[0]) {
|
|
3493
|
+
return rb_str_new2(rbce->unsafe_destination_error);
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
return Qnil;
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3499
|
+
#ifdef HAVE_CURLOPT_UNIX_SOCKET_PATH
|
|
3500
|
+
/*
|
|
3501
|
+
* call-seq:
|
|
3502
|
+
* easy.unix_socket_path => string or nil
|
|
3503
|
+
*
|
|
3504
|
+
* Return the configured Unix socket path, if set through CURLOPT_UNIX_SOCKET_PATH.
|
|
3505
|
+
*/
|
|
3506
|
+
static VALUE ruby_curl_easy_unix_socket_path_get(VALUE self) {
|
|
3507
|
+
ruby_curl_easy *rbce;
|
|
3508
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
3509
|
+
|
|
3510
|
+
return rb_easy_get("unix_socket_path");
|
|
3511
|
+
}
|
|
3512
|
+
#endif
|
|
3513
|
+
|
|
2497
3514
|
/*
|
|
2498
3515
|
* call-seq:
|
|
2499
3516
|
* easy.http_version = Curl::HTTP_1_1 => Curl::HTTP_1_1
|
|
@@ -2802,13 +3819,32 @@ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap, int _c, const VALUE *_pt
|
|
|
2802
3819
|
return resolve_string;
|
|
2803
3820
|
}
|
|
2804
3821
|
|
|
3822
|
+
/***********************************************
|
|
3823
|
+
* This is an rb_iterate callback used to set up the connect-to list.
|
|
3824
|
+
*/
|
|
3825
|
+
static VALUE cb_each_connect_to(VALUE connect_to, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
|
|
3826
|
+
struct curl_slist **list;
|
|
3827
|
+
VALUE connect_to_string;
|
|
3828
|
+
TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
|
|
3829
|
+
|
|
3830
|
+
connect_to_string = rb_obj_as_string(connect_to);
|
|
3831
|
+
struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(connect_to_string));
|
|
3832
|
+
if (!new_list) {
|
|
3833
|
+
rb_raise(rb_eNoMemError, "Failed to append to connect-to list");
|
|
3834
|
+
}
|
|
3835
|
+
*list = new_list;
|
|
3836
|
+
|
|
3837
|
+
return connect_to_string;
|
|
3838
|
+
}
|
|
3839
|
+
|
|
2805
3840
|
/***********************************************
|
|
2806
3841
|
*
|
|
2807
3842
|
* Setup a connection
|
|
2808
3843
|
*
|
|
2809
3844
|
* Always returns Qtrue, rb_raise on error.
|
|
2810
3845
|
*/
|
|
2811
|
-
VALUE
|
|
3846
|
+
static VALUE ruby_curl_easy_setup_body(VALUE arg) {
|
|
3847
|
+
ruby_curl_easy *rbce = (ruby_curl_easy *)arg;
|
|
2812
3848
|
// TODO this could do with a bit of refactoring...
|
|
2813
3849
|
CURL *curl;
|
|
2814
3850
|
VALUE url, _url = rb_easy_get("url");
|
|
@@ -2816,9 +3852,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
2816
3852
|
struct curl_slist **phdrs = &(rbce->curl_proxy_headers);
|
|
2817
3853
|
struct curl_slist **cmds = &(rbce->curl_ftp_commands);
|
|
2818
3854
|
struct curl_slist **rslv = &(rbce->curl_resolve);
|
|
3855
|
+
struct curl_slist **cnto = &(rbce->curl_connect_to);
|
|
3856
|
+
int public_policy_disables_proxy;
|
|
2819
3857
|
|
|
2820
3858
|
curl = rbce->curl;
|
|
3859
|
+
public_policy_disables_proxy = rbce->network_policy == CURB_NETWORK_POLICY_PUBLIC && !rbce->allow_proxy;
|
|
2821
3860
|
rbce->callback_error = Qnil;
|
|
3861
|
+
rbce->unsafe_destination_blocked = 0;
|
|
3862
|
+
memset(rbce->unsafe_destination_error, 0, CURL_ERROR_SIZE);
|
|
2822
3863
|
|
|
2823
3864
|
if (_url == Qnil) {
|
|
2824
3865
|
rb_raise(eCurlErrError, "No URL supplied");
|
|
@@ -2827,6 +3868,60 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
2827
3868
|
url = rb_check_string_type(_url);
|
|
2828
3869
|
curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
|
|
2829
3870
|
|
|
3871
|
+
#ifdef HAVE_CURLOPT_DOH_URL
|
|
3872
|
+
curl_easy_setopt(curl, CURLOPT_DOH_URL, rb_easy_nil("doh_url") ? NULL : rb_easy_get_str("doh_url"));
|
|
3873
|
+
#endif
|
|
3874
|
+
|
|
3875
|
+
#ifdef HAVE_CURLOPT_DNS_SERVERS
|
|
3876
|
+
if (rbce->network_policy == CURB_NETWORK_POLICY_PUBLIC && !rb_easy_nil("dns_servers")) {
|
|
3877
|
+
rb_raise(eCurlErrUnsafeDestination, "DNS server overrides are disabled by public network policy");
|
|
3878
|
+
}
|
|
3879
|
+
curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, rb_easy_nil("dns_servers") ? NULL : rb_easy_get_str("dns_servers"));
|
|
3880
|
+
#endif
|
|
3881
|
+
|
|
3882
|
+
#ifdef CURB_HAVE_PREREQ_HOST_POLICY
|
|
3883
|
+
if (!rb_easy_nil("allowed_hosts")) {
|
|
3884
|
+
curb_prepare_network_allowed_hosts(rbce);
|
|
3885
|
+
curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, curb_host_allowlist_prereq);
|
|
3886
|
+
curl_easy_setopt(curl, CURLOPT_PREREQDATA, rbce);
|
|
3887
|
+
} else {
|
|
3888
|
+
curb_clear_network_allowed_hosts(rbce);
|
|
3889
|
+
curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, NULL);
|
|
3890
|
+
curl_easy_setopt(curl, CURLOPT_PREREQDATA, NULL);
|
|
3891
|
+
}
|
|
3892
|
+
#else
|
|
3893
|
+
curb_clear_network_allowed_hosts(rbce);
|
|
3894
|
+
#endif
|
|
3895
|
+
|
|
3896
|
+
#ifdef CURB_HAVE_OPENSOCKET_NETWORK_POLICY
|
|
3897
|
+
if (rbce->network_policy == CURB_NETWORK_POLICY_PUBLIC) {
|
|
3898
|
+
curb_prepare_network_allowed_cidr_rules(rbce);
|
|
3899
|
+
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, curb_public_network_opensocket);
|
|
3900
|
+
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, rbce);
|
|
3901
|
+
#ifdef HAVE_CURLOPT_FRESH_CONNECT
|
|
3902
|
+
curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1L);
|
|
3903
|
+
#endif
|
|
3904
|
+
#ifdef HAVE_CURLOPT_FORBID_REUSE
|
|
3905
|
+
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L);
|
|
3906
|
+
#endif
|
|
3907
|
+
#ifdef HAVE_CURLOPT_UNIX_SOCKET_PATH
|
|
3908
|
+
if (!rbce->allow_unix_socket) {
|
|
3909
|
+
curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, NULL);
|
|
3910
|
+
}
|
|
3911
|
+
#endif
|
|
3912
|
+
} else {
|
|
3913
|
+
curb_clear_network_allowed_cidr_rules(rbce);
|
|
3914
|
+
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, NULL);
|
|
3915
|
+
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, NULL);
|
|
3916
|
+
#ifdef HAVE_CURLOPT_FRESH_CONNECT
|
|
3917
|
+
curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 0L);
|
|
3918
|
+
#endif
|
|
3919
|
+
#ifdef HAVE_CURLOPT_FORBID_REUSE
|
|
3920
|
+
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, rbce->forbid_reuse_set ? rbce->forbid_reuse : 0L);
|
|
3921
|
+
#endif
|
|
3922
|
+
}
|
|
3923
|
+
#endif
|
|
3924
|
+
|
|
2830
3925
|
// network stuff and auth
|
|
2831
3926
|
if (!rb_easy_nil("interface_hm")) {
|
|
2832
3927
|
curl_easy_setopt(curl, CURLOPT_INTERFACE, rb_easy_get_str("interface_hm"));
|
|
@@ -2858,13 +3953,17 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
2858
3953
|
curl_easy_setopt(curl, CURLOPT_USERPWD, NULL);
|
|
2859
3954
|
}
|
|
2860
3955
|
|
|
2861
|
-
if (
|
|
3956
|
+
if (public_policy_disables_proxy) {
|
|
3957
|
+
curl_easy_setopt(curl, CURLOPT_PROXY, "");
|
|
3958
|
+
} else if (rb_easy_nil("proxy_url")) {
|
|
2862
3959
|
curl_easy_setopt(curl, CURLOPT_PROXY, NULL);
|
|
2863
3960
|
} else {
|
|
2864
3961
|
curl_easy_setopt(curl, CURLOPT_PROXY, rb_easy_get_str("proxy_url"));
|
|
2865
3962
|
}
|
|
2866
3963
|
|
|
2867
|
-
if (
|
|
3964
|
+
if (public_policy_disables_proxy) {
|
|
3965
|
+
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, NULL);
|
|
3966
|
+
} else if (rb_easy_nil("proxypwd")) {
|
|
2868
3967
|
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, NULL);
|
|
2869
3968
|
} else {
|
|
2870
3969
|
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, rb_easy_get_str("proxypwd"));
|
|
@@ -2879,6 +3978,8 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
2879
3978
|
#endif
|
|
2880
3979
|
|
|
2881
3980
|
// body/header procs
|
|
3981
|
+
rbce->downloaded_body_bytes = 0;
|
|
3982
|
+
|
|
2882
3983
|
if (!rb_easy_nil("body_proc")) {
|
|
2883
3984
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)&proc_data_handler_body);
|
|
2884
3985
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, rbce);
|
|
@@ -2949,7 +4050,8 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
2949
4050
|
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, rbce->max_redirs);
|
|
2950
4051
|
#endif
|
|
2951
4052
|
|
|
2952
|
-
curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL,
|
|
4053
|
+
curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL,
|
|
4054
|
+
public_policy_disables_proxy ? 0L : rbce->proxy_tunnel);
|
|
2953
4055
|
curl_easy_setopt(curl, CURLOPT_FILETIME, rbce->fetch_file_time);
|
|
2954
4056
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, rbce->ssl_verify_peer);
|
|
2955
4057
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, rbce->ssl_verify_host);
|
|
@@ -3112,6 +4214,11 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
3112
4214
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, rb_easy_get_str("useragent"));
|
|
3113
4215
|
}
|
|
3114
4216
|
|
|
4217
|
+
/* Setup can be rerun for safety-policy changes while a handle is attached.
|
|
4218
|
+
* Rebuild slist-backed options from Ruby opts instead of appending to stale
|
|
4219
|
+
* native lists. */
|
|
4220
|
+
ruby_curl_easy_clear_setup_lists(rbce);
|
|
4221
|
+
|
|
3115
4222
|
/* Setup HTTP headers if necessary */
|
|
3116
4223
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); // XXX: maybe we shouldn't be clearing this?
|
|
3117
4224
|
|
|
@@ -3189,8 +4296,44 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
3189
4296
|
}
|
|
3190
4297
|
#endif
|
|
3191
4298
|
|
|
4299
|
+
#ifdef HAVE_CURLOPT_CONNECT_TO
|
|
4300
|
+
/* Setup connect-to list if necessary */
|
|
4301
|
+
if (!rb_easy_nil("connect_to")) {
|
|
4302
|
+
if (rb_easy_type_check("connect_to", T_ARRAY)) {
|
|
4303
|
+
VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, cnto);
|
|
4304
|
+
rb_block_call(rb_easy_get("connect_to"), rb_intern("each"), 0, NULL, cb_each_connect_to, wrap);
|
|
4305
|
+
} else {
|
|
4306
|
+
VALUE connect_to_str = rb_obj_as_string(rb_easy_get("connect_to"));
|
|
4307
|
+
struct curl_slist *new_list = curl_slist_append(*cnto, StringValuePtr(connect_to_str));
|
|
4308
|
+
if (!new_list) {
|
|
4309
|
+
rb_raise(rb_eNoMemError, "Failed to append to connect-to list");
|
|
4310
|
+
}
|
|
4311
|
+
*cnto = new_list;
|
|
4312
|
+
}
|
|
4313
|
+
|
|
4314
|
+
if (*cnto) {
|
|
4315
|
+
curl_easy_setopt(curl, CURLOPT_CONNECT_TO, *cnto);
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
#endif
|
|
4319
|
+
|
|
3192
4320
|
return Qnil;
|
|
3193
4321
|
}
|
|
4322
|
+
|
|
4323
|
+
VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
4324
|
+
ruby_curl_easy_enter_native(rbce);
|
|
4325
|
+
return rb_ensure(ruby_curl_easy_setup_body, (VALUE)rbce,
|
|
4326
|
+
ruby_curl_easy_leave_native, (VALUE)rbce);
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
static VALUE ruby_curl_easy_setup_self(VALUE self) {
|
|
4330
|
+
ruby_curl_easy *rbce;
|
|
4331
|
+
|
|
4332
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
4333
|
+
ruby_curl_easy_setup(rbce);
|
|
4334
|
+
|
|
4335
|
+
return self;
|
|
4336
|
+
}
|
|
3194
4337
|
/***********************************************
|
|
3195
4338
|
*
|
|
3196
4339
|
* Clean up a connection
|
|
@@ -3202,6 +4345,7 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
|
|
|
3202
4345
|
CURL *curl = rbce->curl;
|
|
3203
4346
|
struct curl_slist *ftp_commands;
|
|
3204
4347
|
struct curl_slist *resolve;
|
|
4348
|
+
struct curl_slist *connect_to;
|
|
3205
4349
|
|
|
3206
4350
|
ruby_curl_easy_clear_headers_list(rbce);
|
|
3207
4351
|
ruby_curl_easy_clear_proxy_headers_list(rbce);
|
|
@@ -3216,6 +4360,14 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
|
|
|
3216
4360
|
ruby_curl_easy_clear_resolve_list(rbce);
|
|
3217
4361
|
}
|
|
3218
4362
|
|
|
4363
|
+
connect_to = rbce->curl_connect_to;
|
|
4364
|
+
if (connect_to) {
|
|
4365
|
+
ruby_curl_easy_clear_connect_to_list(rbce);
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4368
|
+
curb_clear_network_allowed_cidr_rules(rbce);
|
|
4369
|
+
curb_clear_network_allowed_hosts(rbce);
|
|
4370
|
+
|
|
3219
4371
|
/* clean up a PUT request's curl options. */
|
|
3220
4372
|
if (!rb_easy_nil("upload")) {
|
|
3221
4373
|
rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up
|
|
@@ -3350,6 +4502,23 @@ struct easy_form_perform_args {
|
|
|
3350
4502
|
int form_set_on_curl;
|
|
3351
4503
|
};
|
|
3352
4504
|
|
|
4505
|
+
struct easy_join_args {
|
|
4506
|
+
VALUE args_ary;
|
|
4507
|
+
};
|
|
4508
|
+
|
|
4509
|
+
static VALUE join_easy_arguments_body(VALUE argp) {
|
|
4510
|
+
struct easy_join_args *args = (struct easy_join_args *)argp;
|
|
4511
|
+
return rb_funcall(args->args_ary, idJoin, 1, rbstrAmp);
|
|
4512
|
+
}
|
|
4513
|
+
|
|
4514
|
+
static VALUE join_easy_arguments(ruby_curl_easy *rbce, VALUE args_ary) {
|
|
4515
|
+
struct easy_join_args args = { args_ary };
|
|
4516
|
+
|
|
4517
|
+
ruby_curl_easy_enter_native(rbce);
|
|
4518
|
+
return rb_ensure(join_easy_arguments_body, (VALUE)&args,
|
|
4519
|
+
ruby_curl_easy_leave_native, (VALUE)rbce);
|
|
4520
|
+
}
|
|
4521
|
+
|
|
3353
4522
|
static void append_multipart_form_argument(VALUE arg,
|
|
3354
4523
|
struct curl_httppost **first,
|
|
3355
4524
|
struct curl_httppost **last) {
|
|
@@ -3452,7 +4621,7 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
|
|
|
3452
4621
|
} else {
|
|
3453
4622
|
VALUE post_body = Qnil;
|
|
3454
4623
|
/* TODO: check for PostField.file and raise error before to_s fails */
|
|
3455
|
-
if ((post_body =
|
|
4624
|
+
if ((post_body = join_easy_arguments(rbce, args_ary)) == Qnil) {
|
|
3456
4625
|
rb_raise(eCurlErrError, "Failed to join arguments");
|
|
3457
4626
|
return Qnil;
|
|
3458
4627
|
} else {
|
|
@@ -3511,7 +4680,7 @@ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
|
|
|
3511
4680
|
return ret;
|
|
3512
4681
|
} else {
|
|
3513
4682
|
/* Join arguments into a raw PATCH body */
|
|
3514
|
-
VALUE patch_body =
|
|
4683
|
+
VALUE patch_body = join_easy_arguments(rbce, args_ary);
|
|
3515
4684
|
if (patch_body == Qnil) {
|
|
3516
4685
|
rb_raise(eCurlErrError, "Failed to join arguments");
|
|
3517
4686
|
return Qnil;
|
|
@@ -3570,7 +4739,7 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
|
|
|
3570
4739
|
}
|
|
3571
4740
|
/* Fallback: join all arguments */
|
|
3572
4741
|
else {
|
|
3573
|
-
VALUE post_body =
|
|
4742
|
+
VALUE post_body = join_easy_arguments(rbce, args_ary);
|
|
3574
4743
|
if (post_body != Qnil && rb_type(post_body) == T_STRING &&
|
|
3575
4744
|
RSTRING_LEN(post_body) > 0) {
|
|
3576
4745
|
ruby_curl_easy_put_data_set(self, post_body);
|
|
@@ -3600,6 +4769,47 @@ static VALUE ruby_curl_easy_body_str_get(VALUE self) {
|
|
|
3600
4769
|
CURB_OBJECT_HGETTER(ruby_curl_easy, body_data);
|
|
3601
4770
|
}
|
|
3602
4771
|
|
|
4772
|
+
/*
|
|
4773
|
+
* call-seq:
|
|
4774
|
+
* easy.max_body_bytes = bytes_or_nil => bytes_or_nil
|
|
4775
|
+
*
|
|
4776
|
+
* Set an application-level cap for bytes accepted by the body write callback.
|
|
4777
|
+
* +nil+ or 0 clears the cap.
|
|
4778
|
+
*/
|
|
4779
|
+
static VALUE ruby_curl_easy_max_body_bytes_set(VALUE self, VALUE val) {
|
|
4780
|
+
ruby_curl_easy *rbce;
|
|
4781
|
+
long long limit;
|
|
4782
|
+
|
|
4783
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
4784
|
+
|
|
4785
|
+
if (NIL_P(val)) {
|
|
4786
|
+
rb_hash_delete(rbce->opts, rb_easy_hkey("max_body_bytes"));
|
|
4787
|
+
return Qnil;
|
|
4788
|
+
}
|
|
4789
|
+
|
|
4790
|
+
limit = NUM2LL(val);
|
|
4791
|
+
if (limit < 0) {
|
|
4792
|
+
rb_raise(rb_eArgError, "max_body_bytes must be greater than or equal to zero");
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
if (limit == 0) {
|
|
4796
|
+
rb_hash_delete(rbce->opts, rb_easy_hkey("max_body_bytes"));
|
|
4797
|
+
} else {
|
|
4798
|
+
val = LL2NUM(limit);
|
|
4799
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("max_body_bytes"), val);
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
return val;
|
|
4803
|
+
}
|
|
4804
|
+
|
|
4805
|
+
/*
|
|
4806
|
+
* call-seq:
|
|
4807
|
+
* easy.max_body_bytes => bytes_or_nil
|
|
4808
|
+
*/
|
|
4809
|
+
static VALUE ruby_curl_easy_max_body_bytes_get(VALUE self) {
|
|
4810
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, max_body_bytes);
|
|
4811
|
+
}
|
|
4812
|
+
|
|
3603
4813
|
/*
|
|
3604
4814
|
* call-seq:
|
|
3605
4815
|
* easy.header_str => "response header"
|
|
@@ -4307,12 +5517,28 @@ static VALUE ruby_curl_easy_multi_get(VALUE self) {
|
|
|
4307
5517
|
*/
|
|
4308
5518
|
static VALUE ruby_curl_easy_multi_set(VALUE self, VALUE multi) {
|
|
4309
5519
|
ruby_curl_easy *rbce;
|
|
5520
|
+
VALUE old_multi;
|
|
5521
|
+
ruby_curl_multi *old_rbcm;
|
|
5522
|
+
CURLMcode result;
|
|
4310
5523
|
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
4311
5524
|
|
|
4312
5525
|
if (!NIL_P(multi) && rb_obj_is_kind_of(multi, cCurlMulti) != Qtrue) {
|
|
4313
5526
|
rb_raise(rb_eTypeError, "expected Curl::Multi or nil");
|
|
4314
5527
|
}
|
|
4315
5528
|
|
|
5529
|
+
old_multi = rbce->multi;
|
|
5530
|
+
if (old_multi == multi) {
|
|
5531
|
+
return rbce->multi;
|
|
5532
|
+
}
|
|
5533
|
+
|
|
5534
|
+
old_rbcm = ruby_curl_multi_pointer_if_compatible(old_multi);
|
|
5535
|
+
if (old_rbcm) {
|
|
5536
|
+
result = rb_curl_multi_detach_easy(old_rbcm, rbce);
|
|
5537
|
+
if (result != CURLM_OK) {
|
|
5538
|
+
raise_curl_multi_error_exception(result);
|
|
5539
|
+
}
|
|
5540
|
+
}
|
|
5541
|
+
|
|
4316
5542
|
rbce->multi = multi;
|
|
4317
5543
|
return rbce->multi;
|
|
4318
5544
|
}
|
|
@@ -4457,6 +5683,12 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4457
5683
|
/* Forward request-target directly to libcurl as a string. */
|
|
4458
5684
|
curl_easy_setopt(rbce->curl, CURLOPT_REQUEST_TARGET, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
4459
5685
|
} break;
|
|
5686
|
+
#endif
|
|
5687
|
+
#ifdef HAVE_CURLOPT_DOH_URL
|
|
5688
|
+
case CURLOPT_DOH_URL: {
|
|
5689
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("doh_url"), val);
|
|
5690
|
+
curl_easy_setopt(rbce->curl, CURLOPT_DOH_URL, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5691
|
+
} break;
|
|
4460
5692
|
#endif
|
|
4461
5693
|
case CURLOPT_TCP_NODELAY: {
|
|
4462
5694
|
curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
|
|
@@ -4531,7 +5763,9 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4531
5763
|
curl_easy_setopt(rbce->curl, CURLOPT_SSL_CIPHER_LIST, StringValueCStr(val));
|
|
4532
5764
|
} break;
|
|
4533
5765
|
case CURLOPT_FORBID_REUSE: {
|
|
4534
|
-
|
|
5766
|
+
rbce->forbid_reuse = NUM2LONG(val);
|
|
5767
|
+
rbce->forbid_reuse_set = 1;
|
|
5768
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FORBID_REUSE, rbce->forbid_reuse);
|
|
4535
5769
|
} break;
|
|
4536
5770
|
#ifdef HAVE_CURLOPT_GSSAPI_DELEGATION
|
|
4537
5771
|
case CURLOPT_GSSAPI_DELEGATION: {
|
|
@@ -4540,7 +5774,14 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4540
5774
|
#endif
|
|
4541
5775
|
#ifdef HAVE_CURLOPT_UNIX_SOCKET_PATH
|
|
4542
5776
|
case CURLOPT_UNIX_SOCKET_PATH: {
|
|
4543
|
-
|
|
5777
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("unix_socket_path"), val);
|
|
5778
|
+
curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5779
|
+
} break;
|
|
5780
|
+
#endif
|
|
5781
|
+
#ifdef HAVE_CURLOPT_DNS_SERVERS
|
|
5782
|
+
case CURLOPT_DNS_SERVERS: {
|
|
5783
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("dns_servers"), val);
|
|
5784
|
+
curl_easy_setopt(rbce->curl, CURLOPT_DNS_SERVERS, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
4544
5785
|
} break;
|
|
4545
5786
|
#endif
|
|
4546
5787
|
#ifdef HAVE_CURLOPT_MAX_SEND_SPEED_LARGE
|
|
@@ -4558,6 +5799,11 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4558
5799
|
curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE, NUM2LONG(val));
|
|
4559
5800
|
break;
|
|
4560
5801
|
#endif
|
|
5802
|
+
#ifdef HAVE_CURLOPT_MAXFILESIZE_LARGE
|
|
5803
|
+
case CURLOPT_MAXFILESIZE_LARGE:
|
|
5804
|
+
curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE_LARGE, (curl_off_t)NUM2LL(val));
|
|
5805
|
+
break;
|
|
5806
|
+
#endif
|
|
4561
5807
|
#ifdef HAVE_CURLOPT_TCP_KEEPALIVE
|
|
4562
5808
|
case CURLOPT_TCP_KEEPALIVE:
|
|
4563
5809
|
curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPALIVE, NUM2LONG(val));
|
|
@@ -4588,6 +5834,16 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4588
5834
|
case CURLOPT_REDIR_PROTOCOLS:
|
|
4589
5835
|
curl_easy_setopt(rbce->curl, option, NUM2LONG(val));
|
|
4590
5836
|
break;
|
|
5837
|
+
#ifdef HAVE_CURLOPT_PROTOCOLS_STR
|
|
5838
|
+
case CURLOPT_PROTOCOLS_STR:
|
|
5839
|
+
curl_easy_setopt(rbce->curl, CURLOPT_PROTOCOLS_STR, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5840
|
+
break;
|
|
5841
|
+
#endif
|
|
5842
|
+
#ifdef HAVE_CURLOPT_REDIR_PROTOCOLS_STR
|
|
5843
|
+
case CURLOPT_REDIR_PROTOCOLS_STR:
|
|
5844
|
+
curl_easy_setopt(rbce->curl, CURLOPT_REDIR_PROTOCOLS_STR, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5845
|
+
break;
|
|
5846
|
+
#endif
|
|
4591
5847
|
#ifdef HAVE_CURLOPT_SSL_SESSIONID_CACHE
|
|
4592
5848
|
case CURLOPT_SSL_SESSIONID_CACHE:
|
|
4593
5849
|
curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val));
|
|
@@ -4618,6 +5874,21 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4618
5874
|
curl_easy_setopt(rbce->curl, CURLOPT_PROXY_SSL_VERIFYHOST, NUM2LONG(val));
|
|
4619
5875
|
break;
|
|
4620
5876
|
#endif
|
|
5877
|
+
#ifdef HAVE_CURLOPT_DOH_SSL_VERIFYPEER
|
|
5878
|
+
case CURLOPT_DOH_SSL_VERIFYPEER:
|
|
5879
|
+
curl_easy_setopt(rbce->curl, CURLOPT_DOH_SSL_VERIFYPEER, NUM2LONG(val));
|
|
5880
|
+
break;
|
|
5881
|
+
#endif
|
|
5882
|
+
#ifdef HAVE_CURLOPT_DOH_SSL_VERIFYHOST
|
|
5883
|
+
case CURLOPT_DOH_SSL_VERIFYHOST:
|
|
5884
|
+
curl_easy_setopt(rbce->curl, CURLOPT_DOH_SSL_VERIFYHOST, NUM2LONG(val));
|
|
5885
|
+
break;
|
|
5886
|
+
#endif
|
|
5887
|
+
#ifdef HAVE_CURLOPT_DOH_SSL_VERIFYSTATUS
|
|
5888
|
+
case CURLOPT_DOH_SSL_VERIFYSTATUS:
|
|
5889
|
+
curl_easy_setopt(rbce->curl, CURLOPT_DOH_SSL_VERIFYSTATUS, NUM2LONG(val));
|
|
5890
|
+
break;
|
|
5891
|
+
#endif
|
|
4621
5892
|
#ifdef HAVE_CURLOPT_RESOLVE
|
|
4622
5893
|
case CURLOPT_RESOLVE: {
|
|
4623
5894
|
struct curl_slist *list = NULL;
|
|
@@ -4648,6 +5919,34 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4648
5919
|
rb_hash_aset(rbce->opts, rb_easy_hkey("resolve"), val);
|
|
4649
5920
|
curl_easy_setopt(rbce->curl, CURLOPT_RESOLVE, list);
|
|
4650
5921
|
} break;
|
|
5922
|
+
#endif
|
|
5923
|
+
#ifdef HAVE_CURLOPT_CONNECT_TO
|
|
5924
|
+
case CURLOPT_CONNECT_TO: {
|
|
5925
|
+
struct curl_slist *list = NULL;
|
|
5926
|
+
ruby_curl_easy_clear_connect_to_list(rbce);
|
|
5927
|
+
if (NIL_P(val)) {
|
|
5928
|
+
list = NULL;
|
|
5929
|
+
} else if (TYPE(val) == T_ARRAY) {
|
|
5930
|
+
long i, len = RARRAY_LEN(val);
|
|
5931
|
+
for (i = 0; i < len; i++) {
|
|
5932
|
+
VALUE item = rb_ary_entry(val, i);
|
|
5933
|
+
struct curl_slist *new_list = curl_slist_append(list, StringValueCStr(item));
|
|
5934
|
+
if (!new_list) {
|
|
5935
|
+
curl_slist_free_all(list);
|
|
5936
|
+
rb_raise(rb_eNoMemError, "Failed to append to connect-to list");
|
|
5937
|
+
}
|
|
5938
|
+
list = new_list;
|
|
5939
|
+
}
|
|
5940
|
+
} else {
|
|
5941
|
+
list = curl_slist_append(NULL, StringValueCStr(val));
|
|
5942
|
+
if (!list) {
|
|
5943
|
+
rb_raise(rb_eNoMemError, "Failed to create connect-to list");
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
rbce->curl_connect_to = list;
|
|
5947
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("connect_to"), val);
|
|
5948
|
+
curl_easy_setopt(rbce->curl, CURLOPT_CONNECT_TO, list);
|
|
5949
|
+
} break;
|
|
4651
5950
|
#endif
|
|
4652
5951
|
default:
|
|
4653
5952
|
rb_raise(rb_eTypeError, "Curb unsupported option");
|
|
@@ -4773,6 +6072,8 @@ static VALUE ruby_curl_easy_error_message(VALUE klass, VALUE code) {
|
|
|
4773
6072
|
void init_curb_easy() {
|
|
4774
6073
|
idCall = rb_intern("call");
|
|
4775
6074
|
idJoin = rb_intern("join");
|
|
6075
|
+
idNetworkPolicyNone = rb_intern("none");
|
|
6076
|
+
idNetworkPolicyPublic = rb_intern("public");
|
|
4776
6077
|
|
|
4777
6078
|
rbstrAmp = rb_str_new2("&");
|
|
4778
6079
|
rb_global_variable(&rbstrAmp);
|
|
@@ -4823,6 +6124,17 @@ void init_curb_easy() {
|
|
|
4823
6124
|
rb_define_method(cCurlEasy, "ftp_commands", ruby_curl_easy_ftp_commands_get, 0);
|
|
4824
6125
|
rb_define_method(cCurlEasy, "resolve=", ruby_curl_easy_resolve_set, 1);
|
|
4825
6126
|
rb_define_method(cCurlEasy, "resolve", ruby_curl_easy_resolve_get, 0);
|
|
6127
|
+
rb_define_method(cCurlEasy, "connect_to=", ruby_curl_easy_connect_to_set, 1);
|
|
6128
|
+
rb_define_method(cCurlEasy, "connect_to", ruby_curl_easy_connect_to_get, 0);
|
|
6129
|
+
rb_define_method(cCurlEasy, "doh_url=", ruby_curl_easy_doh_url_set, 1);
|
|
6130
|
+
rb_define_method(cCurlEasy, "doh_url", ruby_curl_easy_doh_url_get, 0);
|
|
6131
|
+
#ifdef HAVE_CURLOPT_DNS_SERVERS
|
|
6132
|
+
rb_define_method(cCurlEasy, "dns_servers", ruby_curl_easy_dns_servers_get, 0);
|
|
6133
|
+
#endif
|
|
6134
|
+
rb_define_method(cCurlEasy, "allowed_cidrs=", ruby_curl_easy_allowed_cidrs_set, 1);
|
|
6135
|
+
rb_define_method(cCurlEasy, "allowed_cidrs", ruby_curl_easy_allowed_cidrs_get, 0);
|
|
6136
|
+
rb_define_method(cCurlEasy, "allowed_hosts=", ruby_curl_easy_allowed_hosts_set, 1);
|
|
6137
|
+
rb_define_method(cCurlEasy, "allowed_hosts", ruby_curl_easy_allowed_hosts_get, 0);
|
|
4826
6138
|
|
|
4827
6139
|
rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1);
|
|
4828
6140
|
rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0);
|
|
@@ -4896,6 +6208,8 @@ void init_curb_easy() {
|
|
|
4896
6208
|
rb_define_method(cCurlEasy, "ignore_content_length?", ruby_curl_easy_ignore_content_length_q, 0);
|
|
4897
6209
|
rb_define_method(cCurlEasy, "resolve_mode", ruby_curl_easy_resolve_mode, 0);
|
|
4898
6210
|
rb_define_method(cCurlEasy, "resolve_mode=", ruby_curl_easy_resolve_mode_set, 1);
|
|
6211
|
+
rb_define_method(cCurlEasy, "network_policy", ruby_curl_easy_network_policy_get, 0);
|
|
6212
|
+
rb_define_method(cCurlEasy, "network_policy=", ruby_curl_easy_network_policy_set, 1);
|
|
4899
6213
|
|
|
4900
6214
|
rb_define_method(cCurlEasy, "on_body", ruby_curl_easy_on_body_set, -1);
|
|
4901
6215
|
rb_define_method(cCurlEasy, "on_header", ruby_curl_easy_on_header_set, -1);
|
|
@@ -4914,6 +6228,8 @@ void init_curb_easy() {
|
|
|
4914
6228
|
|
|
4915
6229
|
/* Post-perform info methods */
|
|
4916
6230
|
rb_define_method(cCurlEasy, "body_str", ruby_curl_easy_body_str_get, 0);
|
|
6231
|
+
rb_define_method(cCurlEasy, "max_body_bytes=", ruby_curl_easy_max_body_bytes_set, 1);
|
|
6232
|
+
rb_define_method(cCurlEasy, "max_body_bytes", ruby_curl_easy_max_body_bytes_get, 0);
|
|
4917
6233
|
rb_define_method(cCurlEasy, "header_str", ruby_curl_easy_header_str_get, 0);
|
|
4918
6234
|
|
|
4919
6235
|
rb_define_method(cCurlEasy, "last_effective_url", ruby_curl_easy_last_effective_url_get, 0);
|
|
@@ -4964,9 +6280,16 @@ void init_curb_easy() {
|
|
|
4964
6280
|
|
|
4965
6281
|
rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0);
|
|
4966
6282
|
rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1);
|
|
6283
|
+
rb_define_private_method(cCurlEasy, "__curb_native_setup!", ruby_curl_easy_setup_self, 0);
|
|
6284
|
+
rb_define_private_method(cCurlEasy, "__curb_allow_proxy=", ruby_curl_easy_allow_proxy_set, 1);
|
|
6285
|
+
rb_define_private_method(cCurlEasy, "__curb_allow_unix_socket=", ruby_curl_easy_allow_unix_socket_set, 1);
|
|
4967
6286
|
rb_define_private_method(cCurlEasy, "_take_callback_error", ruby_curl_easy_take_callback_error, 0);
|
|
4968
6287
|
rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0);
|
|
4969
6288
|
rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0);
|
|
6289
|
+
rb_define_method(cCurlEasy, "unsafe_destination_error", ruby_curl_easy_unsafe_destination_error_get, 0);
|
|
6290
|
+
#ifdef HAVE_CURLOPT_UNIX_SOCKET_PATH
|
|
6291
|
+
rb_define_method(cCurlEasy, "unix_socket_path", ruby_curl_easy_unix_socket_path_get, 0);
|
|
6292
|
+
#endif
|
|
4970
6293
|
|
|
4971
6294
|
rb_define_method(cCurlEasy, "setopt", ruby_curl_easy_set_opt, 2);
|
|
4972
6295
|
rb_define_method(cCurlEasy, "getinfo", ruby_curl_easy_get_opt, 1);
|