plunder 2.0.0a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/agent +48 -0
- data/bin/plunder +158 -0
- data/ext/fsdb/extconf.rb +13 -0
- data/ext/fsdb/fsdb-c.c +134 -0
- data/ext/fsdb/fsdb-c.h +49 -0
- data/ext/fsdb/fsdb.c +267 -0
- data/ext/fsdb/hash.c +140 -0
- data/ext/fsdb/hash.h +52 -0
- data/ext/fsdb/test.c +90 -0
- data/ext/fsdb/utilities.c +125 -0
- data/ext/fsdb/utilities.h +32 -0
- data/ext/fsdb/vault.c +78 -0
- data/ext/fsdb/vault.h +44 -0
- data/ext/smbclient/extconf.rb +32 -0
- data/ext/smbclient/smb.c +234 -0
- data/ext/smbclient/smb.h +61 -0
- data/ext/smbclient/smbclient.c +116 -0
- data/lib/core/agent.rb +67 -0
- data/lib/core/analyzer.rb +84 -0
- data/lib/core/client.rb +163 -0
- data/lib/core/commands.rb +53 -0
- data/lib/core/config.rb +103 -0
- data/lib/core/io.rb +84 -0
- data/lib/core/plunder.rb +208 -0
- data/lib/core/report.rb +78 -0
- data/lib/core/target.rb +33 -0
- data/lib/core/tree.rb +59 -0
- metadata +74 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: bbebd78c8c112b0e33711b45fa10d1e6eabb4de4
|
|
4
|
+
data.tar.gz: 8acf065be1107b051e3434db977f03bd3e4074c1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 232c90294a83f26e97de70ab32fb2f301592d1ef19656fb10a0a0a0c3d6482153c956905d2f1fe591b7837ab7d07425926d43b3cecc00c0324d5e010d1a212b2
|
|
7
|
+
data.tar.gz: 928179b760f52ece7f2a6883adf2a5fbd6b82530bc9eb31bb664c2158fc6a926e7e93aaf507c089b58a9b2e24ce180ea45df7f4e52e11d0be7cbb05d22dc6770
|
data/bin/agent
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# -*- mode: ruby; -*-
|
|
3
|
+
#
|
|
4
|
+
# Plunder - SMB scanning and auditing tool
|
|
5
|
+
# Copyright (C) 2017 Joshua Stone
|
|
6
|
+
#
|
|
7
|
+
# This program is free software; you can redistribute it and/or
|
|
8
|
+
# modify it under the terms of the GNU General Public License
|
|
9
|
+
# as published by the Free Software Foundation; either version 2
|
|
10
|
+
# of the License, or (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU General Public License
|
|
18
|
+
# along with this program; if not, write to the Free Software
|
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
20
|
+
#
|
|
21
|
+
|
|
22
|
+
require_relative '../ext/smbclient/smbclient.so'
|
|
23
|
+
|
|
24
|
+
domain = STDIN.readline.chomp
|
|
25
|
+
user = STDIN.readline.chomp
|
|
26
|
+
pass = STDIN.readline.chomp
|
|
27
|
+
|
|
28
|
+
client = SMBClient.new(domain, user, pass)
|
|
29
|
+
|
|
30
|
+
STDIN.each_line do |line|
|
|
31
|
+
begin
|
|
32
|
+
listing = client.list(line.chomp)
|
|
33
|
+
if listing == nil
|
|
34
|
+
STDOUT.puts "E failure"
|
|
35
|
+
else
|
|
36
|
+
(dirs, files) = client.list(line.chomp)
|
|
37
|
+
dirs.each { |dir| STDOUT.puts "D #{dir}" }
|
|
38
|
+
files.each { |file| STDOUT.puts "F #{file}" }
|
|
39
|
+
end
|
|
40
|
+
rescue Exception => e
|
|
41
|
+
log = open("agent.log", "a")
|
|
42
|
+
log.puts "Agent exception: #{e.class}:#{e.message}"
|
|
43
|
+
log.close
|
|
44
|
+
ensure
|
|
45
|
+
STDOUT.puts "C done"
|
|
46
|
+
STDOUT.flush
|
|
47
|
+
end
|
|
48
|
+
end
|
data/bin/plunder
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# -*- mode: ruby; -*-
|
|
3
|
+
#
|
|
4
|
+
# Plunder - SMB scanning and auditing tool
|
|
5
|
+
# Copyright (C) 2017 Joshua Stone
|
|
6
|
+
#
|
|
7
|
+
# This program is free software; you can redistribute it and/or
|
|
8
|
+
# modify it under the terms of the GNU General Public License
|
|
9
|
+
# as published by the Free Software Foundation; either version 2
|
|
10
|
+
# of the License, or (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU General Public License
|
|
18
|
+
# along with this program; if not, write to the Free Software
|
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
20
|
+
#
|
|
21
|
+
|
|
22
|
+
$pry = false
|
|
23
|
+
|
|
24
|
+
require 'time'
|
|
25
|
+
require 'set'
|
|
26
|
+
require 'pry'
|
|
27
|
+
require 'yaml'
|
|
28
|
+
require 'uri'
|
|
29
|
+
|
|
30
|
+
require_relative '../lib/core/io.rb'
|
|
31
|
+
require_relative '../lib/core/commands.rb'
|
|
32
|
+
require_relative '../lib/core/plunder.rb'
|
|
33
|
+
require_relative '../lib/core/config.rb'
|
|
34
|
+
require_relative '../lib/core/client.rb'
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
require 'pry'
|
|
38
|
+
$pry = true
|
|
39
|
+
rescue LoadError
|
|
40
|
+
puts
|
|
41
|
+
Plunder::Report.error("'pry' gem not found, debugging will be less fun!")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Plunder::PlunderIO.banner
|
|
45
|
+
|
|
46
|
+
if ARGV.length == 0
|
|
47
|
+
Plunder::PlunderIO.usage
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
cmd = Plunder::Commands.new do
|
|
52
|
+
command("init") do
|
|
53
|
+
name = next_arg
|
|
54
|
+
path = name + ".yaml"
|
|
55
|
+
default = Plunder::Config.default(name)
|
|
56
|
+
open(path, "w") { |file| file.write(default.to_yaml) }
|
|
57
|
+
puts " [+] wrote default config to file '\x1b[32;1m#{path}\x1b[0m'"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
command("scan") do
|
|
61
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
62
|
+
Plunder::Report.init(plunder.name)
|
|
63
|
+
begin
|
|
64
|
+
Plunder::Report.notify("<mag>#{Time::now.to_s}<rst> : Starting scan")
|
|
65
|
+
plunder.scan(next_arg.to_i)
|
|
66
|
+
ensure
|
|
67
|
+
Plunder::Report.notify("<mag>#{Time::now.to_s}<rst> : Completed scan")
|
|
68
|
+
plunder.save
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
command("creds") do
|
|
73
|
+
config = Plunder::Config.new(next_arg + ".yaml")
|
|
74
|
+
config[:domain] = next_arg.strip
|
|
75
|
+
config[:user] = next_arg.strip
|
|
76
|
+
config[:pass] = next_arg.strip
|
|
77
|
+
Plunder::Report.notify("Saved creds")
|
|
78
|
+
config.save
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
command("summary") do
|
|
82
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
83
|
+
plunder.load
|
|
84
|
+
domain = plunder.config[:domain]
|
|
85
|
+
user = plunder.config[:user]
|
|
86
|
+
pass = plunder.config[:pass]
|
|
87
|
+
Plunder::Report.notify("Credentials: '<cya>#{domain}<gra>\\<cya>#{user}<gra>'%'<cya>#{pass}<gra>'<rst>")
|
|
88
|
+
Plunder::Report.notify("Target Count: #{plunder.config[:targets].length}")
|
|
89
|
+
Plunder::Report.notify("Files indexed: #{plunder.fsdb.length}")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
command("target") do
|
|
93
|
+
config = Plunder::Config.new(next_arg + ".yaml")
|
|
94
|
+
url = "smb://#{next_arg}/"
|
|
95
|
+
config.add_target(url)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
command("targets") do
|
|
99
|
+
config = Plunder::Config.new(next_arg + ".yaml")
|
|
100
|
+
stream = STDIN
|
|
101
|
+
stream = open(next_arg) if @args.length > 0
|
|
102
|
+
stream.each_line do |line|
|
|
103
|
+
config.add_target("smb://#{line.chomp}/")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
command("search") do
|
|
108
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
109
|
+
plunder.load
|
|
110
|
+
plunder.select_files(next_arg) do |file, id|
|
|
111
|
+
printf("FILE %-8d %s\n", id, URI::decode(file))
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
command("mirror") do
|
|
116
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
117
|
+
plunder.load
|
|
118
|
+
loaded = Set.new
|
|
119
|
+
stream = STDIN
|
|
120
|
+
stream = open(next_arg) if @args.length > 0
|
|
121
|
+
stream.each_line do |line|
|
|
122
|
+
url = plunder.fsdb[line.to_i]
|
|
123
|
+
unless loaded.member? url
|
|
124
|
+
plunder.mirror(url)
|
|
125
|
+
puts " [-] Mirrored '\x1b[32;1m#{url}\x1b[0m'"
|
|
126
|
+
loaded << url
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
command("listing") do
|
|
132
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
133
|
+
plunder.load
|
|
134
|
+
0.upto(plunder.fsdb.length - 1) do |index|
|
|
135
|
+
printf("FILE %-8d %s\n", index, plunder.fsdb[index])
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
command("client") do
|
|
140
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
141
|
+
plunder.load
|
|
142
|
+
client = Plunder::Client.new(plunder)
|
|
143
|
+
puts ""
|
|
144
|
+
client.repl
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if $pry
|
|
148
|
+
command("debug") do
|
|
149
|
+
plunder = Plunder::Plunder.new(next_arg)
|
|
150
|
+
plunder.load
|
|
151
|
+
binding.pry
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
cmd.run ARGV
|
|
157
|
+
|
|
158
|
+
puts ""
|
data/ext/fsdb/extconf.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Loads mkmf which is used to make makefiles for Ruby extensions
|
|
2
|
+
require 'mkmf'
|
|
3
|
+
|
|
4
|
+
$CFLAGS << ' -Wno-unused-result '
|
|
5
|
+
|
|
6
|
+
# Give it a name
|
|
7
|
+
extension_name = 'fsdb'
|
|
8
|
+
|
|
9
|
+
# The destination
|
|
10
|
+
dir_config(extension_name)
|
|
11
|
+
|
|
12
|
+
# Do the work
|
|
13
|
+
create_makefile(extension_name)
|
data/ext/fsdb/fsdb-c.c
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Plunder - SMB scanning and auditing tool
|
|
3
|
+
// Copyright (C) 2017 Joshua Stone
|
|
4
|
+
//
|
|
5
|
+
// This program is free software; you can redistribute it and/or
|
|
6
|
+
// modify it under the terms of the GNU General Public License
|
|
7
|
+
// as published by the Free Software Foundation; either version 2
|
|
8
|
+
// of the License, or (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU General Public License
|
|
16
|
+
// along with this program; if not, write to the Free Software
|
|
17
|
+
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
//
|
|
19
|
+
|
|
20
|
+
#include "fsdb-c.h"
|
|
21
|
+
|
|
22
|
+
fsdb fsdb_init(int bins) {
|
|
23
|
+
fsdb f;
|
|
24
|
+
|
|
25
|
+
f.strings = vault_init(1024);
|
|
26
|
+
f.paths = vault_init(1024);
|
|
27
|
+
f.index = vault_init(1024);
|
|
28
|
+
f.hash = hash_init(bins);
|
|
29
|
+
f.count = 0;
|
|
30
|
+
|
|
31
|
+
return f;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
uint32_t fsdb_insert_path(fsdb *f, char *path) {
|
|
35
|
+
uint32_t ret;
|
|
36
|
+
uint32_t indices[MAXPATHLEN];
|
|
37
|
+
char *cursor = path + 6;
|
|
38
|
+
char *base = cursor;
|
|
39
|
+
int pindex = 0;
|
|
40
|
+
uint32_t value = 0;
|
|
41
|
+
|
|
42
|
+
while(*cursor) {
|
|
43
|
+
if(*cursor == '/') {
|
|
44
|
+
*cursor = 0;
|
|
45
|
+
if(!match(f->hash, base, f->strings, &value)) {
|
|
46
|
+
value = vault_insert_string(&f->strings, base);
|
|
47
|
+
hash_put(&f->hash, base, value);
|
|
48
|
+
}
|
|
49
|
+
indices[pindex++] = value;
|
|
50
|
+
base = cursor + 1;
|
|
51
|
+
}
|
|
52
|
+
cursor++;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
indices[pindex] = 0x48544150;
|
|
56
|
+
|
|
57
|
+
value = vault_insert_bytes(&f->paths, indices, (pindex + 1) * 4);
|
|
58
|
+
ret = vault_insert_bytes(&f->index, &value, 4);
|
|
59
|
+
f->count++;
|
|
60
|
+
return ret;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
char *fsdb_get_id(fsdb f, int id) {
|
|
64
|
+
char *name;
|
|
65
|
+
int offset;
|
|
66
|
+
int len;
|
|
67
|
+
|
|
68
|
+
len = DEF_NAME_LEN;
|
|
69
|
+
name = (char *) malloc(len + 1);
|
|
70
|
+
offset = 0;
|
|
71
|
+
|
|
72
|
+
uint32_t path = *((uint32_t *)vault_get(f.index, id * 4));
|
|
73
|
+
uint32_t *cursor = (uint32_t *)vault_get(f.paths, path);
|
|
74
|
+
|
|
75
|
+
offset += snprintf(name, len - offset, "smb:/");
|
|
76
|
+
while(*cursor != 0x48544150) {
|
|
77
|
+
uint8_t *elt = vault_get(f.strings, *cursor++);
|
|
78
|
+
int bytes = strlen((char *)elt);
|
|
79
|
+
|
|
80
|
+
while(offset + bytes + 2 > len) {
|
|
81
|
+
len = (len << 1) - (len >> 1);
|
|
82
|
+
name = realloc(name, len + 1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
name[offset++] = '/';
|
|
86
|
+
memcpy(name + offset, elt, bytes);
|
|
87
|
+
offset += bytes;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
name[offset] = 0;
|
|
91
|
+
|
|
92
|
+
return name;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
uint32_t fsdb_intern(fsdb *f, char *s) {
|
|
96
|
+
uint32_t value = 0;
|
|
97
|
+
|
|
98
|
+
if(!match(f->hash, s, f->strings, &value)) {
|
|
99
|
+
value = vault_insert_string(&f->strings, s);
|
|
100
|
+
hash_put(&f->hash, s, value);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
int match(hash h, char *s, vault v, uint32_t *val) {
|
|
107
|
+
int len = strlen(s);
|
|
108
|
+
cons *c;
|
|
109
|
+
|
|
110
|
+
if((c = hash_get(h, s))) {
|
|
111
|
+
while(c) {
|
|
112
|
+
if(strncmp(s, (char *)vault_get(v, c->index), len) == 0) {
|
|
113
|
+
*val = c->index;
|
|
114
|
+
return 1;
|
|
115
|
+
}
|
|
116
|
+
c = c->next;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// note that the above may fail if there are already colliding
|
|
121
|
+
// entries in the hash, but none of them are the same as the
|
|
122
|
+
// current target. So we need to fall through here to make
|
|
123
|
+
// sure "false positives" get handled appropriately.
|
|
124
|
+
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
void fsdb_free(fsdb *f) {
|
|
129
|
+
vault_free(f->strings);
|
|
130
|
+
vault_free(f->paths);
|
|
131
|
+
vault_free(f->index);
|
|
132
|
+
hash_free(f->hash);
|
|
133
|
+
free(f);
|
|
134
|
+
}
|
data/ext/fsdb/fsdb-c.h
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Plunder - SMB scanning and auditing tool
|
|
3
|
+
// Copyright (C) 2017 Joshua Stone
|
|
4
|
+
//
|
|
5
|
+
// This program is free software; you can redistribute it and/or
|
|
6
|
+
// modify it under the terms of the GNU General Public License
|
|
7
|
+
// as published by the Free Software Foundation; either version 2
|
|
8
|
+
// of the License, or (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU General Public License
|
|
16
|
+
// along with this program; if not, write to the Free Software
|
|
17
|
+
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
//
|
|
19
|
+
|
|
20
|
+
#ifndef FSDB_C_H
|
|
21
|
+
#define FSDB_C_H
|
|
22
|
+
|
|
23
|
+
#define MAXPATHLEN 256
|
|
24
|
+
#define DEF_NAME_LEN 128
|
|
25
|
+
|
|
26
|
+
#include <stdio.h>
|
|
27
|
+
#include <stdlib.h>
|
|
28
|
+
#include <stdint.h>
|
|
29
|
+
|
|
30
|
+
#include "vault.h"
|
|
31
|
+
#include "hash.h"
|
|
32
|
+
#include "utilities.h"
|
|
33
|
+
|
|
34
|
+
typedef struct {
|
|
35
|
+
vault strings;
|
|
36
|
+
vault paths;
|
|
37
|
+
vault index;
|
|
38
|
+
hash hash;
|
|
39
|
+
uint32_t count;
|
|
40
|
+
} fsdb;
|
|
41
|
+
|
|
42
|
+
fsdb fsdb_init(int bins);
|
|
43
|
+
uint32_t fsdb_insert_path(fsdb *f, char *path);
|
|
44
|
+
void fsdb_free(fsdb *f);
|
|
45
|
+
uint32_t fsdb_intern(fsdb *f, char *s);
|
|
46
|
+
int match(hash h, char *s, vault v, uint32_t *val);
|
|
47
|
+
char *fsdb_get_id(fsdb f, int id);
|
|
48
|
+
|
|
49
|
+
#endif
|
data/ext/fsdb/fsdb.c
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Plunder - SMB scanning and auditing tool
|
|
3
|
+
// Copyright (C) 2017 Joshua Stone
|
|
4
|
+
//
|
|
5
|
+
// This program is free software; you can redistribute it and/or
|
|
6
|
+
// modify it under the terms of the GNU General Public License
|
|
7
|
+
// as published by the Free Software Foundation; either version 2
|
|
8
|
+
// of the License, or (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU General Public License
|
|
16
|
+
// along with this program; if not, write to the Free Software
|
|
17
|
+
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
//
|
|
19
|
+
|
|
20
|
+
#include <ruby.h>
|
|
21
|
+
#include <ctype.h>
|
|
22
|
+
#include "fsdb-c.h"
|
|
23
|
+
|
|
24
|
+
VALUE fsdb_klass = Qnil;
|
|
25
|
+
|
|
26
|
+
void Init_fsdb();
|
|
27
|
+
VALUE method_init(VALUE self);
|
|
28
|
+
VALUE method_setup(VALUE self);
|
|
29
|
+
VALUE method_put(VALUE self, VALUE string);
|
|
30
|
+
VALUE method_debug(VALUE self);
|
|
31
|
+
VALUE method_get(VALUE self, VALUE num);
|
|
32
|
+
VALUE method_length(VALUE self);
|
|
33
|
+
VALUE method_contains(VALUE self, VALUE string);
|
|
34
|
+
VALUE method_intern(VALUE self, VALUE string);
|
|
35
|
+
VALUE method_lookup(VALUE self, VALUE num);
|
|
36
|
+
VALUE method_write(VALUE self, VALUE path);
|
|
37
|
+
VALUE method_read(VALUE self, VALUE path);
|
|
38
|
+
VALUE method_each_name(VALUE self);
|
|
39
|
+
VALUE method_select_index(VALUE self, VALUE target);
|
|
40
|
+
|
|
41
|
+
void Init_fsdb() {
|
|
42
|
+
fsdb_klass = rb_define_class("FSDB", rb_cObject);
|
|
43
|
+
|
|
44
|
+
rb_define_singleton_method(fsdb_klass, "new", method_setup, 0);
|
|
45
|
+
rb_define_singleton_method(fsdb_klass, "read", method_read, 1);
|
|
46
|
+
|
|
47
|
+
rb_define_method(fsdb_klass, "put", method_put, 1);
|
|
48
|
+
rb_define_method(fsdb_klass, "[]", method_get, 1);
|
|
49
|
+
rb_define_method(fsdb_klass, "debug", method_debug, 0);
|
|
50
|
+
rb_define_method(fsdb_klass, "length", method_length, 0);
|
|
51
|
+
rb_define_method(fsdb_klass, "contains", method_contains, 1);
|
|
52
|
+
rb_define_method(fsdb_klass, "intern", method_intern, 1);
|
|
53
|
+
rb_define_method(fsdb_klass, "lookup", method_lookup, 1);
|
|
54
|
+
rb_define_method(fsdb_klass, "write", method_write, 1);
|
|
55
|
+
rb_define_method(fsdb_klass, "each_name", method_each_name, 0);
|
|
56
|
+
rb_define_method(fsdb_klass, "select_index", method_select_index, 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
VALUE method_setup(VALUE self) {
|
|
60
|
+
fsdb *f;
|
|
61
|
+
f = (fsdb *) malloc(sizeof(fsdb));
|
|
62
|
+
*f = fsdb_init(1 << 19);
|
|
63
|
+
return Data_Wrap_Struct(fsdb_klass, NULL, fsdb_free, f);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
VALUE method_put(VALUE self, VALUE string) {
|
|
67
|
+
char *s = StringValueCStr(string);
|
|
68
|
+
fsdb *f;
|
|
69
|
+
Data_Get_Struct(self, fsdb, f);
|
|
70
|
+
return UINT2NUM((fsdb_insert_path(f, s) / sizeof(uint32_t)) - 1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
VALUE method_debug(VALUE self) {
|
|
74
|
+
fsdb *f;
|
|
75
|
+
Data_Get_Struct(self, fsdb, f);
|
|
76
|
+
printf(" # Count: %d\n", f->count);
|
|
77
|
+
vault_info(f->strings, "Strings");
|
|
78
|
+
hexdump(f->strings.base, 256);
|
|
79
|
+
|
|
80
|
+
vault_info(f->paths, "Paths");
|
|
81
|
+
hexdump(f->paths.base, 256);
|
|
82
|
+
|
|
83
|
+
vault_info(f->index, "Index");
|
|
84
|
+
hexdump(f->index.base, 256);
|
|
85
|
+
|
|
86
|
+
hash_info(f->hash);
|
|
87
|
+
return Qtrue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
VALUE method_get(VALUE self, VALUE num) {
|
|
91
|
+
fsdb *f;
|
|
92
|
+
VALUE ret;
|
|
93
|
+
unsigned int i;
|
|
94
|
+
|
|
95
|
+
i = NUM2UINT(num) + 1;
|
|
96
|
+
Data_Get_Struct(self, fsdb, f);
|
|
97
|
+
|
|
98
|
+
if(i > 0 && i <= f->count) {
|
|
99
|
+
char *name = fsdb_get_id(*f, i);
|
|
100
|
+
ret = rb_str_new_cstr(name);
|
|
101
|
+
free(name);
|
|
102
|
+
return ret;
|
|
103
|
+
} else {
|
|
104
|
+
return Qnil;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
VALUE method_contains(VALUE self, VALUE string) {
|
|
109
|
+
char *s = StringValueCStr(string);
|
|
110
|
+
fsdb *f;
|
|
111
|
+
int i;
|
|
112
|
+
VALUE ret = rb_eval_string_protect("[]", &i);
|
|
113
|
+
char *buffer;
|
|
114
|
+
int buflen;
|
|
115
|
+
|
|
116
|
+
buflen = 1024;
|
|
117
|
+
buffer = (char *)malloc(buflen);
|
|
118
|
+
|
|
119
|
+
Data_Get_Struct(self, fsdb, f);
|
|
120
|
+
for(i = 1; i <= f->count; i++) {
|
|
121
|
+
char *name = fsdb_get_id(*f, i);
|
|
122
|
+
int len = strlen(name);
|
|
123
|
+
|
|
124
|
+
while(len + 1 > buflen) {
|
|
125
|
+
buflen = buflen * 2;
|
|
126
|
+
buffer = realloc(buffer, buflen + 1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
strncpy(buffer, name, len + 1);
|
|
130
|
+
for(char *p = buffer; *p; p++)
|
|
131
|
+
*p = tolower(*p);
|
|
132
|
+
|
|
133
|
+
if(strstr(buffer, s)) {
|
|
134
|
+
rb_funcall(ret, rb_intern("<<"), 1, rb_str_new_cstr(name));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
free(name);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return ret;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
VALUE method_length(VALUE self) {
|
|
144
|
+
fsdb *f;
|
|
145
|
+
Data_Get_Struct(self, fsdb, f);
|
|
146
|
+
return UINT2NUM(f->count);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
VALUE method_intern(VALUE self, VALUE string) {
|
|
150
|
+
char *s;
|
|
151
|
+
fsdb *f;
|
|
152
|
+
|
|
153
|
+
s = StringValueCStr(string);
|
|
154
|
+
Data_Get_Struct(self, fsdb, f);
|
|
155
|
+
|
|
156
|
+
return UINT2NUM(fsdb_intern(f, s));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
VALUE method_lookup(VALUE self, VALUE num) {
|
|
160
|
+
fsdb *f;
|
|
161
|
+
char *s;
|
|
162
|
+
|
|
163
|
+
Data_Get_Struct(self, fsdb, f);
|
|
164
|
+
s = (char *)vault_get(f->strings, NUM2UINT(num));
|
|
165
|
+
|
|
166
|
+
return rb_str_new_cstr(s);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
VALUE method_write(VALUE self, VALUE path) {
|
|
170
|
+
char *p = StringValueCStr(path);
|
|
171
|
+
fsdb *f;
|
|
172
|
+
FILE *out;
|
|
173
|
+
Data_Get_Struct(self, fsdb, f);
|
|
174
|
+
|
|
175
|
+
if(!(out = fopen(p, "wb"))) {
|
|
176
|
+
return Qnil;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fwrite(f, sizeof(fsdb), 1, out);
|
|
180
|
+
fwrite(f->strings.base, 1, f->strings.size, out);
|
|
181
|
+
fwrite(f->paths.base, 1, f->paths.size, out);
|
|
182
|
+
fwrite(f->index.base, 1, f->index.size, out);
|
|
183
|
+
hash_write(f->hash, out);
|
|
184
|
+
|
|
185
|
+
fclose(out);
|
|
186
|
+
|
|
187
|
+
return Qtrue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
VALUE method_read(VALUE self, VALUE path) {
|
|
191
|
+
char *p = StringValueCStr(path);
|
|
192
|
+
fsdb *f;
|
|
193
|
+
FILE *in;
|
|
194
|
+
|
|
195
|
+
f = (fsdb *) malloc(sizeof(fsdb));
|
|
196
|
+
|
|
197
|
+
if(!(in = fopen(p, "rb"))) {
|
|
198
|
+
return Qnil;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fread(f, sizeof(fsdb), 1, in);
|
|
202
|
+
|
|
203
|
+
f->strings.base = (uint8_t *) malloc(f->strings.size);
|
|
204
|
+
f->paths.base = (uint8_t *) malloc(f->paths.size);
|
|
205
|
+
f->index.base = (uint8_t *) malloc(f->index.size);
|
|
206
|
+
|
|
207
|
+
fread(f->strings.base, 1, f->strings.size, in);
|
|
208
|
+
fread(f->paths.base, 1, f->paths.size, in);
|
|
209
|
+
fread(f->index.base, 1, f->index.size, in);
|
|
210
|
+
f->hash = hash_read(in);
|
|
211
|
+
|
|
212
|
+
fclose(in);
|
|
213
|
+
|
|
214
|
+
return Data_Wrap_Struct(fsdb_klass, NULL, fsdb_free, f);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
VALUE method_each_name(VALUE self) {
|
|
218
|
+
if(rb_block_given_p()) {
|
|
219
|
+
fsdb *f;
|
|
220
|
+
char *top, *cursor;
|
|
221
|
+
|
|
222
|
+
Data_Get_Struct(self, fsdb, f);
|
|
223
|
+
cursor = (char *)(f->strings.base + 4);
|
|
224
|
+
top = f->strings.top + ((char *)f->strings.base);
|
|
225
|
+
|
|
226
|
+
while(cursor < top) {
|
|
227
|
+
int len = strlen(cursor);
|
|
228
|
+
VALUE name = rb_str_new(cursor, len);
|
|
229
|
+
VALUE num = UINT2NUM(cursor - ((char *)f->strings.base));
|
|
230
|
+
rb_yield_values(2, name, num);
|
|
231
|
+
cursor += len + 1;
|
|
232
|
+
}
|
|
233
|
+
return Qtrue;
|
|
234
|
+
}
|
|
235
|
+
return Qnil;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
VALUE method_select_index(VALUE self, VALUE target) {
|
|
239
|
+
uint32_t index = 0;
|
|
240
|
+
uint32_t value = NUM2UINT(target);
|
|
241
|
+
uint32_t sep = 0x48544150;
|
|
242
|
+
|
|
243
|
+
if(rb_block_given_p()) {
|
|
244
|
+
fsdb *f;
|
|
245
|
+
uint32_t *top, *cursor;
|
|
246
|
+
|
|
247
|
+
Data_Get_Struct(self, fsdb, f);
|
|
248
|
+
|
|
249
|
+
cursor = ((uint32_t *)(f->paths.base)) + 1;
|
|
250
|
+
top = (uint32_t *)(f->paths.top + ((char *)f->paths.base));
|
|
251
|
+
|
|
252
|
+
while(cursor < top) {
|
|
253
|
+
fflush(stdout);
|
|
254
|
+
while(*cursor != sep) {
|
|
255
|
+
if(*cursor == value && *(cursor+1) == sep) {
|
|
256
|
+
rb_yield(UINT2NUM(index));
|
|
257
|
+
}
|
|
258
|
+
cursor++;
|
|
259
|
+
}
|
|
260
|
+
cursor++;
|
|
261
|
+
index++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return Qtrue;
|
|
265
|
+
}
|
|
266
|
+
return Qnil;
|
|
267
|
+
}
|