geoip2_c 0.4.0 → 0.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08ccfe015bb26a4af300f07f11c20ff027b4de7ee76413755df7d8a7c1e74822'
4
- data.tar.gz: 282c25e8603af880c2bd7dc081c7792c1337a09d6421c4738bc78f02b005b158
3
+ metadata.gz: 25a17a86c77f7e092f8f24a224cd8a57d6bba8595f51d0e052e3a846a3fd67e8
4
+ data.tar.gz: 1bb129e6fba2ff42f583dff127ba4168d958c12af6a7c1521d23a07fbf59dab8
5
5
  SHA512:
6
- metadata.gz: 8bdd737733f0dff3b8691c5b33fcb823b03111af2c09c0fac3c59e12430c55a5b1c46fd9fd19d3fd1a5caa26559f280ad291031d05af76383e67f69450260ff7
7
- data.tar.gz: 773ce3df6dbf835c00267c24c45588efdfc81a2934b839741d80445f401fa0dba56025863745fec01939b4f1fbba3b755e80130697e584a78eff7f3ed023a1c2
6
+ metadata.gz: 22d90d27edebbf88c8d0a9ec2edc304fc0265b1f6814012ca47e4cd5b9ef5f8422fe3b92a63ed90bbba1f3a2eb874c98d1f52f9d28d246f8df83f5d19e3a4819
7
+ data.tar.gz: 4ba29b9f46963e6be9c298c686885b6224e4b7346069da485972d48ac91f0baf025fa882461df943461a0798102a3429cd165d7c13340c7c1aecae576e0fbf97
@@ -0,0 +1,23 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'monthly'
7
+ groups:
8
+ # PR: "Security update [package] from [old] to [new]"
9
+ # This PR should be merged in hurry
10
+ security-updates:
11
+ applies-to: security-updates
12
+ patterns:
13
+ - '*'
14
+
15
+ # PR: "Bump [package] from [old] to [new]"
16
+ # No need to be merged this PR in hurry. It is enough to merge
17
+ # once in a month.
18
+ monthly-updates:
19
+ applies-to: version-updates
20
+ patterns:
21
+ - '*'
22
+ # Allow to create PR both of security and normal updates.
23
+ open-pull-requests-limit: 1
@@ -1,24 +1,33 @@
1
1
  name: ubuntu
2
2
 
3
3
  on:
4
- - push
5
- - pull_request
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ schedule:
9
+ - cron: '0 0 1 * *'
6
10
 
7
11
  jobs:
12
+ ruby-versions:
13
+ uses: ruby/actions/.github/workflows/ruby_versions.yml@master
14
+ with:
15
+ engine: cruby
16
+ min_version: 3.0
8
17
  build:
18
+ needs: ruby-versions
9
19
  runs-on: ubuntu-latest
10
20
  strategy:
11
21
  matrix:
12
- ruby:
13
- - "3.2"
14
- - "3.1"
15
- - "3.0"
22
+ ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
23
+ exclude:
24
+ - ruby: head
16
25
  steps:
17
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
18
27
  with:
19
28
  submodules: true
20
29
  - name: Setup ruby
21
- uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
30
+ uses: ruby/setup-ruby@0dafeac902942906541bc140009cdbf32665b601 # v1.315.0
22
31
  with:
23
32
  ruby-version: ${{ matrix.ruby }}
24
33
  bundler: latest
@@ -1,17 +1,27 @@
1
1
  name: windows
2
2
 
3
3
  on:
4
- - push
5
- - pull_request
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ schedule:
9
+ - cron: '0 0 1 * *'
6
10
 
7
11
  jobs:
12
+ ruby-versions:
13
+ uses: ruby/actions/.github/workflows/ruby_versions.yml@master
14
+ with:
15
+ engine: cruby
16
+ min_version: 3.1
8
17
  build:
18
+ needs: ruby-versions
9
19
  runs-on: windows-latest
10
20
  strategy:
11
21
  matrix:
12
- ruby:
13
- - "3.2"
14
- - "3.1"
22
+ ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
23
+ exclude:
24
+ - ruby: head
15
25
  include:
16
26
  - ruby: "3.0.3"
17
27
  # On Ruby 3.0, we need to use fiddle 1.0.8 or later to retrieve correct
@@ -23,7 +33,7 @@ jobs:
23
33
  # * https://github.com/oneclick/rubyinstaller2/blob/8225034c22152d8195bc0aabc42a956c79d6c712/lib/ruby_installer/build/dll_directory.rb
24
34
  ruby-lib-opt: RUBYLIB=%RUNNER_TOOL_CACHE%/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/fiddle-1.1.0/lib
25
35
  steps:
26
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
36
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
37
  with:
28
38
  submodules: true
29
39
  - name: Install dependencies
@@ -31,7 +41,7 @@ jobs:
31
41
  ridk enable
32
42
  pacman -Sy --noconfirm mingw-w64-x86_64-libmaxminddb
33
43
  - name: Setup ruby
34
- uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0
44
+ uses: ruby/setup-ruby@0dafeac902942906541bc140009cdbf32665b601 # v1.315.0
35
45
  with:
36
46
  ruby-version: ${{ matrix.ruby }}
37
47
  bundler: latest
data/ext/geoip2/geoip2.c CHANGED
@@ -33,6 +33,23 @@ typedef struct {
33
33
  bool symbolize_keys;
34
34
  } ParseRequest;
35
35
 
36
+ /* A LookupResult holds a copy of the libmaxminddb lookup result (whose
37
+ * entry.mmdb points into the owning Database's MMDB_s) together with a
38
+ * reference to that Database VALUE. Keeping the reference and marking it
39
+ * prevents the Database (and its mmap'd file) from being freed while a
40
+ * LookupResult derived from it is still alive. */
41
+ typedef struct {
42
+ MMDB_lookup_result_s result;
43
+ VALUE db;
44
+ } LookupResult;
45
+
46
+ static void
47
+ lookup_result_mark(void *ptr)
48
+ {
49
+ LookupResult *lr = (LookupResult *)ptr;
50
+ rb_gc_mark(lr->db);
51
+ }
52
+
36
53
  static const rb_data_type_t rb_mmdb_type = {
37
54
  "geoip2/mmdb", {
38
55
  0, mmdb_free, 0,
@@ -42,11 +59,25 @@ static const rb_data_type_t rb_mmdb_type = {
42
59
 
43
60
  static const rb_data_type_t rb_lookup_result_type = {
44
61
  "geoip2/lookup_result", {
45
- 0, RUBY_DEFAULT_FREE, 0,
62
+ lookup_result_mark, RUBY_DEFAULT_FREE, 0,
46
63
  }, NULL, NULL,
47
64
  RUBY_TYPED_FREE_IMMEDIATELY
48
65
  };
49
66
 
67
+ /* Fetch the LookupResult and ensure its backing database is still usable.
68
+ * Raises rather than dereferencing a freed/unmapped MMDB_s (use-after-free). */
69
+ static LookupResult *
70
+ lookup_result_get_open(VALUE self)
71
+ {
72
+ LookupResult *lr;
73
+ TypedData_Get_Struct(self, LookupResult, &rb_lookup_result_type, lr);
74
+ if (lr->result.entry.mmdb == NULL ||
75
+ mmdb_is_closed((MMDB_s *)lr->result.entry.mmdb)) {
76
+ rb_raise(rb_eGeoIP2Error, "the database is closed");
77
+ }
78
+ return lr;
79
+ }
80
+
50
81
  static void
51
82
  mmdb_open(const char *db_path, MMDB_s *mmdb)
52
83
  {
@@ -141,10 +172,20 @@ mmdb_entry_data_decode(MMDB_entry_data_s *entry_data)
141
172
  case MMDB_DATA_TYPE_UINT64:
142
173
  return UINT2NUM(entry_data->uint64);
143
174
  case MMDB_DATA_TYPE_UINT128:
175
+ /* uint128 does not fit in a C unsigned long, so build the Integer from the
176
+ * full 128 bits instead of truncating it through UINT2NUM. */
144
177
  #if !(MMDB_UINT128_IS_BYTE_ARRAY)
145
- return UINT2NUM(entry_data->uint128);
178
+ {
179
+ mmdb_uint128_t value = entry_data->uint128;
180
+ return rb_integer_unpack(&value, 1, sizeof(value), 0,
181
+ INTEGER_PACK_NATIVE_BYTE_ORDER |
182
+ INTEGER_PACK_LSWORD_FIRST);
183
+ }
146
184
  #else
147
- rb_raise(rb_eNotImpError, "TODO: unit8_t[16] -> Integer");
185
+ /* entry_data->uint128 is a big-endian uint8_t[16]. */
186
+ return rb_integer_unpack(entry_data->uint128, 16, 1, 0,
187
+ INTEGER_PACK_BIG_ENDIAN |
188
+ INTEGER_PACK_MSWORD_FIRST);
148
189
  #endif
149
190
  case MMDB_DATA_TYPE_ARRAY:
150
191
  /* TODO: not implemented */
@@ -266,6 +307,14 @@ rb_geoip2_db_open_mmdb(VALUE self, VALUE path)
266
307
  db_path = StringValueCStr(path);
267
308
 
268
309
  TypedData_Get_Struct(self, struct MMDB_s, &rb_mmdb_type, mmdb);
310
+
311
+ /* Reopening over an already-open database would overwrite the MMDB_s in
312
+ * place and leak libmaxminddb's internal allocations (and the previous
313
+ * mmap). Close the current database first. */
314
+ if (!mmdb_is_closed(mmdb)) {
315
+ mmdb_close(mmdb);
316
+ }
317
+
269
318
  mmdb_open(db_path, mmdb);
270
319
 
271
320
  return Qnil;
@@ -291,7 +340,7 @@ rb_geoip2_db_lookup(VALUE self, VALUE ip)
291
340
  char *ip_str;
292
341
  MMDB_s *mmdb;
293
342
  MMDB_lookup_result_s result;
294
- MMDB_lookup_result_s *result_ptr;
343
+ LookupResult *result_ptr;
295
344
  VALUE obj;
296
345
 
297
346
  Check_Type(ip, T_STRING);
@@ -305,12 +354,11 @@ rb_geoip2_db_lookup(VALUE self, VALUE ip)
305
354
  }
306
355
 
307
356
  obj = TypedData_Make_Struct(rb_cGeoIP2LookupResult,
308
- struct MMDB_lookup_result_s,
357
+ LookupResult,
309
358
  &rb_lookup_result_type,
310
359
  result_ptr);
311
- result_ptr->found_entry = result.found_entry;
312
- result_ptr->entry = result.entry;
313
- result_ptr->netmask = result.netmask;
360
+ result_ptr->result = result;
361
+ result_ptr->db = self;
314
362
 
315
363
  rb_iv_set(obj, "@symbolize_keys", rb_iv_get(self, "@symbolize_keys"));
316
364
 
@@ -321,11 +369,12 @@ static VALUE
321
369
  rb_geoip2_lr_alloc(VALUE klass)
322
370
  {
323
371
  VALUE obj;
324
- MMDB_lookup_result_s *result;
372
+ LookupResult *result;
325
373
  obj = TypedData_Make_Struct(klass,
326
- struct MMDB_lookup_result_s,
374
+ LookupResult,
327
375
  &rb_lookup_result_type,
328
376
  result);
377
+ result->db = Qnil;
329
378
  return obj;
330
379
  }
331
380
 
@@ -351,13 +400,15 @@ rb_geoip2_lr_get_value(int argc, VALUE *argv, VALUE self)
351
400
  VALUE arg;
352
401
  VALUE rest;
353
402
  char **path;
403
+ VALUE path_tmp;
354
404
  int i = 0;
355
- MMDB_lookup_result_s *result = NULL;
405
+ LookupResult *result = NULL;
356
406
  MMDB_entry_s entry;
357
407
  MMDB_entry_data_s entry_data;
358
408
  char *tmp;
359
409
  VALUE e;
360
410
  int status;
411
+ VALUE val;
361
412
 
362
413
  rb_scan_args(argc, argv, "1*", &arg, &rest);
363
414
  switch(TYPE(arg)) {
@@ -370,12 +421,14 @@ rb_geoip2_lr_get_value(int argc, VALUE *argv, VALUE self)
370
421
  rb_raise(rb_eArgError, "Expected a String or a Symbol");
371
422
  break;
372
423
  }
373
- path = malloc(sizeof(char *) * (RARRAY_LEN(rest) + 2));
374
424
 
375
- TypedData_Get_Struct(self,
376
- struct MMDB_lookup_result_s,
377
- &rb_lookup_result_type,
378
- result);
425
+ result = lookup_result_get_open(self);
426
+
427
+ /* ALLOCV_N ties the allocation to path_tmp: it is released automatically even
428
+ * if a later key conversion raises (e.g. a non-String key, or a String with
429
+ * an embedded NUL that makes StringValueCStr raise), and it raises
430
+ * NoMemoryError instead of returning NULL on allocation failure. */
431
+ path = ALLOCV_N(char *, path_tmp, RARRAY_LEN(rest) + 2);
379
432
 
380
433
  path[i] = rb_geoip2_lr_arg_convert_to_cstring(arg);
381
434
  while (RARRAY_LEN(rest) != 0) {
@@ -387,18 +440,18 @@ rb_geoip2_lr_get_value(int argc, VALUE *argv, VALUE self)
387
440
 
388
441
  path[i+1] = NULL;
389
442
 
390
- entry = result->entry;
443
+ entry = result->result.entry;
391
444
 
392
445
  status = MMDB_aget_value(&entry, &entry_data, (const char *const *const)path);
393
446
 
394
447
  if (status != MMDB_SUCCESS) {
395
- free(path);
396
448
  /* fprintf(stderr, "%s:%s\n", __FUNCTION__, MMDB_strerror(status)); */
449
+ ALLOCV_END(path_tmp);
397
450
  return Qnil;
398
451
  }
399
452
 
400
453
  if (!entry_data.has_data) {
401
- free(path);
454
+ ALLOCV_END(path_tmp);
402
455
  return Qnil;
403
456
  }
404
457
 
@@ -407,38 +460,38 @@ rb_geoip2_lr_get_value(int argc, VALUE *argv, VALUE self)
407
460
  // FIXME optimize below code
408
461
  VALUE array = rb_ary_new();
409
462
  VALUE hash;
410
- VALUE val;
411
463
  bool symbolize_keys = RTEST(rb_iv_get(self, "@symbolize_keys"));
412
464
  for (int j = 0; path[j] != NULL; j++) {
413
465
  rb_ary_push(array, symbolize_keys ? ID2SYM(rb_intern(path[j])) : rb_str_new_cstr(path[j]));
414
466
  }
415
467
  hash = rb_funcall(self, rb_intern("to_h"), 0);
416
468
  val = rb_apply(hash, rb_intern("dig"), array);
417
- free(path);
469
+ ALLOCV_END(path_tmp);
418
470
  return val;
419
471
  }
420
472
 
421
- free(path);
422
- return mmdb_entry_data_decode(&entry_data);
473
+ val = mmdb_entry_data_decode(&entry_data);
474
+ ALLOCV_END(path_tmp);
475
+ return val;
423
476
  }
424
477
 
425
478
  static VALUE
426
479
  rb_geoip2_lr_netmask(VALUE self)
427
480
  {
428
- MMDB_lookup_result_s *result = NULL;
481
+ LookupResult *result = NULL;
429
482
 
430
483
  TypedData_Get_Struct(self,
431
- struct MMDB_lookup_result_s,
484
+ LookupResult,
432
485
  &rb_lookup_result_type,
433
486
  result);
434
487
 
435
- return UINT2NUM(result->netmask);
488
+ return UINT2NUM(result->result.netmask);
436
489
  }
437
490
 
438
491
  static VALUE
439
492
  rb_geoip2_lr_to_h(VALUE self)
440
493
  {
441
- MMDB_lookup_result_s *result = NULL;
494
+ LookupResult *result = NULL;
442
495
  MMDB_entry_data_list_s *entry_data_list = NULL;
443
496
  VALUE hash;
444
497
  VALUE cache;
@@ -452,11 +505,8 @@ rb_geoip2_lr_to_h(VALUE self)
452
505
  }
453
506
  }
454
507
 
455
- TypedData_Get_Struct(self,
456
- struct MMDB_lookup_result_s,
457
- &rb_lookup_result_type,
458
- result);
459
- status = MMDB_get_entry_data_list(&result->entry, &entry_data_list);
508
+ result = lookup_result_get_open(self);
509
+ status = MMDB_get_entry_data_list(&result->result.entry, &entry_data_list);
460
510
  if (status != MMDB_SUCCESS) {
461
511
  rb_raise(rb_eGeoIP2Error, "%s", MMDB_strerror(status));
462
512
  }
@@ -1,3 +1,3 @@
1
1
  module GeoIP2
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geoip2_c
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - okkez
@@ -101,6 +101,7 @@ extensions:
101
101
  - ext/geoip2/extconf.rb
102
102
  extra_rdoc_files: []
103
103
  files:
104
+ - ".github/dependabot.yml"
104
105
  - ".github/workflows/ubuntu.yml"
105
106
  - ".github/workflows/windows.yml"
106
107
  - ".gitignore"
@@ -141,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
142
  - !ruby/object:Gem::Version
142
143
  version: '0'
143
144
  requirements: []
144
- rubygems_version: 4.0.4
145
+ rubygems_version: 4.0.15
145
146
  specification_version: 4
146
147
  summary: Write a short summary, because Rubygems requires one.
147
148
  test_files: []