karmasphere-client 0.6.4
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.
- data/README +41 -0
- data/bin/rubykarmaclient +135 -0
- data/lib/karmasphere.rb +5 -0
- data/lib/karmasphere/client.rb +116 -0
- data/lib/karmasphere/constants.rb +18 -0
- data/lib/karmasphere/debug.rb +22 -0
- data/lib/karmasphere/query.rb +92 -0
- data/lib/karmasphere/response.rb +116 -0
- data/lib/rubytorrent/bencoding.rb +182 -0
- data/test/test_all.rb +8 -0
- data/test/test_client.rb +264 -0
- data/test/test_query.rb +120 -0
- data/test/test_response.rb +65 -0
- metadata +57 -0
data/README
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Karmasphere Ruby Client Library
|
2
|
+
|
3
|
+
produced by Meng Weng Wong 200606
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
After installation, run
|
8
|
+
|
9
|
+
rubykarmaclient --ip4=127.0.0.2
|
10
|
+
|
11
|
+
You should get back a response from the Karmasphere system
|
12
|
+
showing the reputation of that IP address.
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
|
16
|
+
The Karmasphere Client has been packaged as a rubygem. As
|
17
|
+
it is still in active development, and as the rest of the
|
18
|
+
Karmasphere system is still in alpha, this gem has not yet
|
19
|
+
been uploaded to RubyForge (20060701).
|
20
|
+
|
21
|
+
== User Documentation
|
22
|
+
|
23
|
+
To learn about the Karmasphere system, see http://www.karmasphere.com/userguide/
|
24
|
+
|
25
|
+
== Developer Documentation
|
26
|
+
|
27
|
+
If you are an MTA or Anti-Spam Vendor, see http://www.karmasphere.com/devzone/
|
28
|
+
|
29
|
+
== Bugs
|
30
|
+
|
31
|
+
See http://rt.karmasphere.com/
|
32
|
+
|
33
|
+
== History
|
34
|
+
|
35
|
+
[20060701] Packaged for rake/rubygems by Meng Weng Wong <mengwong@karmasphere.com>.
|
36
|
+
[200606] Originally written by Dave Brown <dagbrown>.
|
37
|
+
|
38
|
+
== License
|
39
|
+
|
40
|
+
This client software is distributed under the same terms as Ruby.
|
41
|
+
|
data/bin/rubykarmaclient
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# mengwong@pobox.com 20060629
|
4
|
+
# Karmasphere client for Ruby
|
5
|
+
|
6
|
+
# usage:
|
7
|
+
# rubykarmaclient --host=slave.karmasphere.com --ip4=127.0.0.2
|
8
|
+
|
9
|
+
# ----------------------------------------------------------
|
10
|
+
# libraries
|
11
|
+
# ----------------------------------------------------------
|
12
|
+
|
13
|
+
require "socket"
|
14
|
+
require "rubygems" # if run from trunk/lib, with ../bin/rubykarmaclient, the dev libs will load instead of the system libs
|
15
|
+
require "rubytorrent/bencoding"
|
16
|
+
require "karmasphere/client"
|
17
|
+
require "karmasphere/query"
|
18
|
+
require "karmasphere/response"
|
19
|
+
|
20
|
+
# ----------------------------------------------------------
|
21
|
+
# options processing
|
22
|
+
# ----------------------------------------------------------
|
23
|
+
|
24
|
+
require "getoptlong"
|
25
|
+
opts = GetoptLong.new(
|
26
|
+
[ "--host" , GetoptLong::REQUIRED_ARGUMENT ],
|
27
|
+
[ "--port" , GetoptLong::REQUIRED_ARGUMENT ],
|
28
|
+
[ "--composite" , GetoptLong::REQUIRED_ARGUMENT ],
|
29
|
+
[ "--combiner" , GetoptLong::REQUIRED_ARGUMENT ],
|
30
|
+
[ "--ip4" , GetoptLong::REQUIRED_ARGUMENT ],
|
31
|
+
[ "--ip6" , GetoptLong::REQUIRED_ARGUMENT ],
|
32
|
+
[ "--domain" , GetoptLong::REQUIRED_ARGUMENT ],
|
33
|
+
[ "--url" , GetoptLong::REQUIRED_ARGUMENT ],
|
34
|
+
[ "--email" , GetoptLong::REQUIRED_ARGUMENT ],
|
35
|
+
[ "--flags" , GetoptLong::REQUIRED_ARGUMENT ],
|
36
|
+
[ "--username" , GetoptLong::REQUIRED_ARGUMENT ],
|
37
|
+
[ "--password" , GetoptLong::REQUIRED_ARGUMENT ],
|
38
|
+
[ "--tcp" , GetoptLong::NO_ARGUMENT ],
|
39
|
+
[ "--help" , GetoptLong::NO_ARGUMENT ],
|
40
|
+
[ "--single" , GetoptLong::NO_ARGUMENT ],
|
41
|
+
[ "--debug", "-d" , GetoptLong::NO_ARGUMENT ]
|
42
|
+
);
|
43
|
+
|
44
|
+
myopts = {};
|
45
|
+
opts.each { |opt, arg| myopts[opt] = arg }
|
46
|
+
|
47
|
+
# ----------------------------------------------------------
|
48
|
+
# symbolic constants
|
49
|
+
# ----------------------------------------------------------
|
50
|
+
|
51
|
+
FL_WANTFACTS = 1
|
52
|
+
DEFAULT_IP = "127.0.0.2"
|
53
|
+
DEFAULT_COMPOSITE = "karmasphere.email-sender"
|
54
|
+
|
55
|
+
if myopts.has_key? '--help' then
|
56
|
+
print "usage: rubykarmaclient
|
57
|
+
[ --username=xxx --password=yyy ]
|
58
|
+
[ --ip4=127.0.0.2 ]
|
59
|
+
[ --domain=example.com ]
|
60
|
+
[ --url=http://www.example.com/phishing/ ]
|
61
|
+
[ --tcp ]
|
62
|
+
[ --composite=karmasphere.email-sender ]
|
63
|
+
"
|
64
|
+
exit;
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# ----------------------------------------------------------
|
69
|
+
# data preparation
|
70
|
+
# ----------------------------------------------------------
|
71
|
+
|
72
|
+
identities = []
|
73
|
+
[ "--ip4", "--ip6", "--domain", "--url", "--email" ].each do |identifier|
|
74
|
+
identities << myopts[identifier] if myopts[identifier];
|
75
|
+
end
|
76
|
+
|
77
|
+
identities << DEFAULT_IP if identities.length == 0;
|
78
|
+
|
79
|
+
print "identifiers = #{identities}\n" if myopts['--debug'];
|
80
|
+
|
81
|
+
# ----------------------------------------------------------
|
82
|
+
# main
|
83
|
+
# ----------------------------------------------------------
|
84
|
+
|
85
|
+
#
|
86
|
+
# set up client
|
87
|
+
#
|
88
|
+
|
89
|
+
clientargs = { }
|
90
|
+
|
91
|
+
if myopts.has_key? '--tcp' then clientargs[:type] = :CONNECTION_TCP else clientargs[:type] = :CONNECTION_UDP end
|
92
|
+
|
93
|
+
if myopts.has_key? '--host' then clientargs[:host] = myopts['--host'] end
|
94
|
+
|
95
|
+
if myopts.has_key? '--username' then clientargs[:principal] = myopts['--username'] end
|
96
|
+
if myopts.has_key? '--password' then clientargs[:credentials] = myopts['--password'] end
|
97
|
+
|
98
|
+
client=Karmasphere::Client.new(clientargs)
|
99
|
+
|
100
|
+
#
|
101
|
+
# set up query
|
102
|
+
#
|
103
|
+
|
104
|
+
query_id = myopts['--id'] || "000";
|
105
|
+
|
106
|
+
queryargs = { :Id => query_id,
|
107
|
+
:Identities => identities }
|
108
|
+
|
109
|
+
if myopts.has_key? '--flags' then
|
110
|
+
queryargs[:Flags] = myopts['--flags']
|
111
|
+
else
|
112
|
+
queryargs[:Flags] = FL_WANTFACTS
|
113
|
+
end
|
114
|
+
|
115
|
+
if myopts.has_key? '--composite' then
|
116
|
+
queryargs[:Composites] = myopts['--composite'].split(',')
|
117
|
+
else
|
118
|
+
queryargs[:Composites] = [ DEFAULT_COMPOSITE ]
|
119
|
+
end
|
120
|
+
|
121
|
+
print "query args: " , queryargs.inspect, "\n" if myopts['--debug'];
|
122
|
+
query = Karmasphere::Query.new( queryargs )
|
123
|
+
print query.inspect, "\n" if myopts['--debug'];
|
124
|
+
|
125
|
+
#
|
126
|
+
# display response
|
127
|
+
#
|
128
|
+
|
129
|
+
response = client.ask(query)
|
130
|
+
|
131
|
+
if myopts['--debug']
|
132
|
+
puts response.instance_variable_get('@hash').inspect
|
133
|
+
end
|
134
|
+
|
135
|
+
puts response
|
data/lib/karmasphere.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# The (mostly-complete) Karmasphere Client
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
require "socket"
|
5
|
+
require "rubytorrent/bencoding"
|
6
|
+
require "stringio"
|
7
|
+
require "fcntl"
|
8
|
+
require "karmasphere/debug"
|
9
|
+
|
10
|
+
module Karmasphere
|
11
|
+
CONNECTION_UDP = :CONNECTION_UDP
|
12
|
+
CONNECTION_TCP = :CONNECTION_TCP
|
13
|
+
|
14
|
+
class CredentialsError < Exception
|
15
|
+
end
|
16
|
+
|
17
|
+
class Client
|
18
|
+
private
|
19
|
+
def send_query(query)
|
20
|
+
if @auth
|
21
|
+
querystr=query.to_hash.merge(@auth).to_bencoding
|
22
|
+
else
|
23
|
+
querystr=query.to_hash.to_bencoding
|
24
|
+
end
|
25
|
+
|
26
|
+
if TCPSocket === @socket then
|
27
|
+
@socket.send [querystr.length].pack("N"),0
|
28
|
+
end
|
29
|
+
|
30
|
+
Karmasphere.dprint("sending query string #{querystr}\n");
|
31
|
+
@socket.send querystr,0
|
32
|
+
Karmasphere.dprint("sent.\n");
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_response(query)
|
36
|
+
# (hack, not guaranteed to work) handle out-of-order responses
|
37
|
+
loop do
|
38
|
+
if @response_pool.has_key? query.id then
|
39
|
+
return @response_pool.delete(query.id)
|
40
|
+
else
|
41
|
+
response=Response.new( decode(receive_response), query )
|
42
|
+
@response_pool[response.id] = response;
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def receive_response
|
48
|
+
if TCPSocket === @socket then
|
49
|
+
length_s = @socket.recvfrom(4)[0]
|
50
|
+
length_n = length_s.unpack("N")[0]
|
51
|
+
Karmasphere.dprint("client decoding TCP response of #{length_n} bytes\n");
|
52
|
+
@socket.recvfrom(length_n)[0]
|
53
|
+
else
|
54
|
+
Karmasphere.dprint("client awaiting response from #@socket\n");
|
55
|
+
reply, from = @socket.recvfrom(8192)
|
56
|
+
Karmasphere.dprint("client response received from #@socket\n");
|
57
|
+
reply
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def decode(querystr)
|
62
|
+
RubyTorrent::BStream.new(
|
63
|
+
StringIO.new( querystr )
|
64
|
+
).to_a[0]
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_query(query)
|
68
|
+
send_query query
|
69
|
+
find_response query
|
70
|
+
end
|
71
|
+
|
72
|
+
public
|
73
|
+
def initialize(*args)
|
74
|
+
host="query.karmasphere.com"
|
75
|
+
port=8666
|
76
|
+
type=CONNECTION_UDP
|
77
|
+
if Hash === args[0] then
|
78
|
+
opts = args[0]
|
79
|
+
host = opts[:host] if opts.has_key? :host
|
80
|
+
port = opts[:port] if opts.has_key? :port
|
81
|
+
type = opts[:type] if opts.has_key? :type and
|
82
|
+
opts[:type] == CONNECTION_TCP ||
|
83
|
+
opts[:type] == CONNECTION_UDP
|
84
|
+
@auth = nil
|
85
|
+
if opts.has_key? :principal then
|
86
|
+
unless opts.has_key? :credentials
|
87
|
+
raise CredentialsError, "principal missing credentials"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
if opts.has_key? :credentials then
|
91
|
+
unless opts.has_key? :principal
|
92
|
+
raise CredentialsError, "credentials missing principal"
|
93
|
+
end
|
94
|
+
@auth = { "a" => [ opts[:principal], opts[:credentials] ] }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if type == CONNECTION_UDP
|
99
|
+
@socket = UDPSocket.new
|
100
|
+
Karmasphere.dprint "UDP client: attempting to connect to #{host}:#{port}\n"
|
101
|
+
@socket.connect host, port
|
102
|
+
@socket.fcntl(Fcntl::F_SETFD,1) if defined? Fcntl::F_SETFD
|
103
|
+
Karmasphere.dprint "UDP client: created socket #@socket\n"
|
104
|
+
else
|
105
|
+
Karmasphere.dprint "TCP client: attempting to connect to #{host}:#{port}\n"
|
106
|
+
@socket = TCPSocket.new host, port
|
107
|
+
end
|
108
|
+
|
109
|
+
@response_pool={}
|
110
|
+
end
|
111
|
+
|
112
|
+
def ask(query)
|
113
|
+
make_query(query)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Constants used in the Karmasphere Query class
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
module Karmasphere
|
6
|
+
IDT_IP4_ADDRESS = 0
|
7
|
+
IDT_IP6_ADDRESS = 1
|
8
|
+
IDT_DOMAIN_NAME = 2
|
9
|
+
IDT_EMAIL_ADDRESS = 3
|
10
|
+
IDT_URL = 4
|
11
|
+
IDT_OPAQUE = 5
|
12
|
+
AUTHENTIC = "a"
|
13
|
+
SMTP_CLIENT_IP = "smtp.client-ip"
|
14
|
+
SMTP_ENV_HELO = "smtp.env.helo"
|
15
|
+
SMTP_ENV_MAIL_FROM = "smtp.env.mail-from"
|
16
|
+
SMTP_ENV_RCPT_TO = "smtp.env.rcpt-to"
|
17
|
+
SMTP_HEADER_FROM_ADDRESS = "smtp.header.from.address"
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Karmasphere
|
3
|
+
|
4
|
+
# if you want debugging turned on,
|
5
|
+
# set any one of these environment variables:
|
6
|
+
# * KARMADEBUG
|
7
|
+
# * RUBYDEBUG
|
8
|
+
# or change the following line to read .find(lambda { true })
|
9
|
+
|
10
|
+
KARMADEBUG = %w{KARMADEBUG RUBYDEBUG}.find(nil) do |debugenv|
|
11
|
+
ENV[debugenv] and ENV[debugenv].size and ENV[debugenv] != "0"
|
12
|
+
end
|
13
|
+
|
14
|
+
# use +dprint+ instead of print. it will print to STDERR,
|
15
|
+
# but only when debugging is turned on.
|
16
|
+
|
17
|
+
def dprint(*args)
|
18
|
+
STDERR.print("KarmaDebug: ", "%0.6f: " % Time.now, *args) if KARMADEBUG
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function :dprint
|
22
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Karmasphere Query class (translation of the Perl code)
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
require "karmasphere/constants"
|
6
|
+
|
7
|
+
module Karmasphere
|
8
|
+
class Query
|
9
|
+
@@ID=0
|
10
|
+
Realflag = {
|
11
|
+
:Id => "_",
|
12
|
+
:Identities => "i",
|
13
|
+
:Composites => "s",
|
14
|
+
:Combiners => "c",
|
15
|
+
:Flags => "fl"
|
16
|
+
}
|
17
|
+
|
18
|
+
private
|
19
|
+
def guess_identity_type(addr)
|
20
|
+
case addr
|
21
|
+
when /^[0-9\.]{7,15}$/: IDT_IP4_ADDRESS
|
22
|
+
when /^[0-9a-f:\/]{2,64}$/i: IDT_IP6_ADDRESS
|
23
|
+
when /^https?:\/\//: IDT_URL
|
24
|
+
when /@/: IDT_EMAIL_ADDRESS
|
25
|
+
when /\./: IDT_DOMAIN_NAME
|
26
|
+
else IDT_OPAQUE
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
|
32
|
+
def initialize(*args)
|
33
|
+
self.id = "q#{@@ID += 1}"
|
34
|
+
|
35
|
+
if args.length > 0
|
36
|
+
opts = args[0] if Hash === args[0]
|
37
|
+
|
38
|
+
[:Id, :Identities, :Composites, :Composite, :Feeds,
|
39
|
+
:Combiners, :Combiner, :Flags].each do |option|
|
40
|
+
|
41
|
+
if opts.has_key? option
|
42
|
+
send("#{option.to_s.downcase}=", opts[option])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_accessor :id, :composites, :feeds, :combiners
|
49
|
+
|
50
|
+
attr_writer :flags
|
51
|
+
def flags
|
52
|
+
@flags.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def composite=(newcomposite)
|
56
|
+
self.composites=newcomposite
|
57
|
+
end
|
58
|
+
|
59
|
+
def feed=(newfeed)
|
60
|
+
@feeds=[newfeed]
|
61
|
+
end
|
62
|
+
|
63
|
+
def identities=(stuff)
|
64
|
+
if String === stuff then
|
65
|
+
return self.identities=[stuff]
|
66
|
+
end
|
67
|
+
|
68
|
+
@identities = if Array === stuff then
|
69
|
+
stuff.map do |item|
|
70
|
+
if Array === item then
|
71
|
+
item
|
72
|
+
else
|
73
|
+
[item, guess_identity_type(item)]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
attr_reader :identities
|
79
|
+
alias identity= identities=
|
80
|
+
|
81
|
+
def to_hash
|
82
|
+
retval={}
|
83
|
+
[:Id, :Identities, :Composites,
|
84
|
+
:Feeds, :Combiners, :Flags].each do |k|
|
85
|
+
if instance_variables.member? "@#{k.to_s.downcase}"
|
86
|
+
retval[Realflag[k]]=send "#{k.to_s.downcase}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
retval
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Desired Output Format
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
# the Java, Perl and C clients all produce the same output format.
|
6
|
+
#
|
7
|
+
# 20060907-15:53:55 mengwong@newyears-air:~/src/karmasphere/trunk/lib% karmaclient --ip4=127.0.0.2 --host=bowie.tx.karmasphere.com
|
8
|
+
# Response id 'q0': 12ms, 1 combinations, 8 facts
|
9
|
+
# Combiner 'karmasphere.emailchecker': verdict 1000 (isipp.iadb-vouched-ip: if-pass(0) => return good(1.0))
|
10
|
+
# Feed 'sorbs.dul' opinion -1000 (Dynamic IP Addresses See: http://www.sorbs.net/lookup.shtml?$)
|
11
|
+
# Feed 'spamhaus.xbl-cbl' opinion -1000 (http://www.spamhaus.org/query/bl?ip=$)
|
12
|
+
# Feed 'cymru.bogons' opinion -1000 (Invalid source IP address (cymru))
|
13
|
+
# Feed 'dsbl.list' opinion -1000 (http://dsbl.org/listing?$)
|
14
|
+
# Feed 'dsbl.multihop' opinion -1000 (http://dsbl.org/listing?$)
|
15
|
+
# Feed 'spamcop.bl' opinion 2147483647 (null data)
|
16
|
+
# Feed 'isipp.iadb-vouched-ip' opinion 1000 (Vouched by ISIPP/IADB)
|
17
|
+
# Feed 'spamhaus.sbl' opinion -1000 (http://www.spamhaus.org/query/bl?ip=$)
|
18
|
+
# 20060907-15:54:01 mengwong@newyears-air:~/src/karmasphere/trunk/lib%
|
19
|
+
|
20
|
+
#------------------------------------------------------------------------
|
21
|
+
# Response class for the Karmasphere client
|
22
|
+
#------------------------------------------------------------------------
|
23
|
+
|
24
|
+
module Karmasphere
|
25
|
+
class Response
|
26
|
+
private
|
27
|
+
def combination_get(key, combiner=nil)
|
28
|
+
if combinations then
|
29
|
+
if !combiner and combinations.has_key?("default") then
|
30
|
+
combination('default')
|
31
|
+
elsif !combiner then
|
32
|
+
combinations.values.find do |combo|
|
33
|
+
if combo.has_key? key then combo[key] end
|
34
|
+
end
|
35
|
+
else
|
36
|
+
my_combination=combination(combiner)
|
37
|
+
if my_combination then
|
38
|
+
return my_combination[key]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
public
|
45
|
+
|
46
|
+
def initialize(hash, query=nil)
|
47
|
+
@hash=hash
|
48
|
+
@query=query
|
49
|
+
end
|
50
|
+
|
51
|
+
def id; @hash["_"] end
|
52
|
+
|
53
|
+
def time; @hash["time"] if @hash.has_key? "time" end
|
54
|
+
|
55
|
+
def facts
|
56
|
+
if @hash.has_key? "facts" then
|
57
|
+
@hash["facts"]
|
58
|
+
elsif @hash.has_key? "f" then
|
59
|
+
@hash["f"]
|
60
|
+
else
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def combinations
|
66
|
+
if @hash.has_key? "c" then
|
67
|
+
@hash["c"]
|
68
|
+
elsif @hash.has_key? "combiners" then
|
69
|
+
@hash["combiners"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def combination(combiner)
|
74
|
+
combinations[combiner]
|
75
|
+
end
|
76
|
+
|
77
|
+
def combiner_names
|
78
|
+
if combinations then
|
79
|
+
combinations.keys
|
80
|
+
else
|
81
|
+
[]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def verdict(name=nil); combination_get "v",name; end
|
86
|
+
alias value verdict
|
87
|
+
def data(name=nil); combination_get "d", name; end
|
88
|
+
|
89
|
+
def error; @hash["error"] if @hash.has_key? "error"; end
|
90
|
+
def message; @hash["message"] if @hash.has_key? "message"; end
|
91
|
+
|
92
|
+
def to_s;
|
93
|
+
"Response_id '#{id}': " +
|
94
|
+
if time then "#{time}ms, " else "" end +
|
95
|
+
"#{combiner_names.length} combinations, " +
|
96
|
+
"#{facts.length} facts\n" +
|
97
|
+
if error then
|
98
|
+
"Error: #{message}\n"
|
99
|
+
else
|
100
|
+
combiner_names.map do |name|
|
101
|
+
"Combiner '#{name}': verdict #{value(name)} " +
|
102
|
+
"(#{data(name) || '(undef)'})\n"
|
103
|
+
end.join("") +
|
104
|
+
facts.map do |fact|
|
105
|
+
"Feed '#{fact['f']}' opinion #{fact['v']} (" +
|
106
|
+
if fact.has_key? "d" then
|
107
|
+
fact["d"]
|
108
|
+
else
|
109
|
+
"null data"
|
110
|
+
end + ")" +
|
111
|
+
if fact.has_key? "i" then " (identity=#{fact['i']})" else "" end + "\n"
|
112
|
+
end.join("")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
## bencoding.rb -- parse and generate bencoded values.
|
2
|
+
## Copyright 2004 William Morgan.
|
3
|
+
##
|
4
|
+
## This file is part of RubyTorrent. RubyTorrent is free software;
|
5
|
+
## you can redistribute it and/or modify it under the terms of version
|
6
|
+
## 2 of the GNU General Public License as published by the Free
|
7
|
+
## Software Foundation.
|
8
|
+
##
|
9
|
+
## RubyTorrent is distributed in the hope that it will be useful, but
|
10
|
+
## WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
## General Public License (in the file COPYING) for more details.
|
13
|
+
|
14
|
+
require 'uri'
|
15
|
+
require 'digest/sha1'
|
16
|
+
|
17
|
+
module RubyTorrent
|
18
|
+
|
19
|
+
## we mess in the users' namespaces in this file. there's no good way
|
20
|
+
## around it. i don't think it's too egregious though.
|
21
|
+
|
22
|
+
class BEncodingError < StandardError; end
|
23
|
+
|
24
|
+
class BStream
|
25
|
+
include Enumerable
|
26
|
+
|
27
|
+
@@classes = []
|
28
|
+
|
29
|
+
def initialize(s)
|
30
|
+
@s = s
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.register_bencoded_class(c)
|
34
|
+
@@classes.push c
|
35
|
+
end
|
36
|
+
|
37
|
+
def each
|
38
|
+
happy = true
|
39
|
+
begin
|
40
|
+
happy = false
|
41
|
+
c = @s.getc
|
42
|
+
@@classes.each do |klass|
|
43
|
+
if klass.bencoded? c
|
44
|
+
o = klass.parse_bencoding(c, @s)
|
45
|
+
happy = true
|
46
|
+
yield o
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end unless c.nil?
|
50
|
+
unless happy
|
51
|
+
@s.ungetc c unless c.nil?
|
52
|
+
end
|
53
|
+
end while happy
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class String
|
61
|
+
def to_bencoding
|
62
|
+
self.length.to_s + ":" + self.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.bencoded?(c)
|
66
|
+
(?0 .. ?9).include? c
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.parse_bencoding(c, s)
|
70
|
+
lens = c.chr
|
71
|
+
while ((x = s.getc) != ?:)
|
72
|
+
unless (?0 .. ?9).include? x
|
73
|
+
s.ungetc x
|
74
|
+
raise RubyTorrent::BEncodingError, "invalid bencoded string length #{lens} + #{x}"
|
75
|
+
end
|
76
|
+
lens += x.chr
|
77
|
+
end
|
78
|
+
raise RubyTorrent::BEncodingError, %{invalid length #{lens} in bencoded string} unless lens.length <= 20
|
79
|
+
len = lens.to_i
|
80
|
+
raise RubyTorrent::BEncodingError, %{invalid length #{lens} in bencoded string} unless len >= 0
|
81
|
+
(len > 0 ? s.read(len) : "")
|
82
|
+
end
|
83
|
+
|
84
|
+
RubyTorrent::BStream.register_bencoded_class self
|
85
|
+
end
|
86
|
+
|
87
|
+
class Integer
|
88
|
+
def to_bencoding
|
89
|
+
"i" + self.to_s + "e"
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.bencoded?(c)
|
93
|
+
c == ?i
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.parse_bencoding(c, s)
|
97
|
+
ints = ""
|
98
|
+
while ((x = s.getc.chr) != 'e')
|
99
|
+
raise RubyTorrent::BEncodingError, "invalid bencoded integer #{x.inspect}" unless x =~ /\d|-/
|
100
|
+
ints += x
|
101
|
+
end
|
102
|
+
raise RubyTorrent::BEncodingError, "invalid integer #{ints} (too long)" unless ints.length <= 20
|
103
|
+
int = ints.to_i
|
104
|
+
raise RubyTorrent::BEncodingError, %{can't parse bencoded integer "#{ints}"} if (int == 0) && (ints !~ /^0$/) #'
|
105
|
+
int
|
106
|
+
end
|
107
|
+
|
108
|
+
RubyTorrent::BStream.register_bencoded_class self
|
109
|
+
end
|
110
|
+
|
111
|
+
class Time
|
112
|
+
def to_bencoding
|
113
|
+
self.to_i.to_bencoding
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module URI
|
118
|
+
def to_bencoding
|
119
|
+
self.to_s.to_bencoding
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Symbol#to_bencoding added by dagbrown 2006/6/3 for karmaclient
|
124
|
+
|
125
|
+
class Symbol
|
126
|
+
def to_bencoding
|
127
|
+
to_s.bencoding
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Array
|
132
|
+
def to_bencoding
|
133
|
+
"l" + self.map { |e| e.to_bencoding }.join + "e"
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.bencoded?(c)
|
137
|
+
c == ?l
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.parse_bencoding(c, s)
|
141
|
+
ret = RubyTorrent::BStream.new(s).map { |x| x }
|
142
|
+
raise RubyTorrent::BEncodingError, "missing list terminator" unless s.getc == ?e
|
143
|
+
ret
|
144
|
+
end
|
145
|
+
|
146
|
+
RubyTorrent::BStream.register_bencoded_class self
|
147
|
+
end
|
148
|
+
|
149
|
+
class Hash
|
150
|
+
def to_bencoding
|
151
|
+
"d" + keys.sort.map do |k|
|
152
|
+
v = self[k]
|
153
|
+
if v.nil?
|
154
|
+
nil
|
155
|
+
else
|
156
|
+
[k.to_bencoding, v.to_bencoding].join
|
157
|
+
end
|
158
|
+
end.compact.join + "e"
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.bencoded?(c)
|
162
|
+
c == ?d
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.parse_bencoding(c, s)
|
166
|
+
ret = {}
|
167
|
+
key = nil
|
168
|
+
RubyTorrent::BStream.new(s).each do |x|
|
169
|
+
if key == nil
|
170
|
+
key = x
|
171
|
+
else
|
172
|
+
ret[key] = x
|
173
|
+
key = nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
raise RubyTorrent::BEncodingError, "no dictionary terminator" unless s.getc == ?e
|
178
|
+
ret
|
179
|
+
end
|
180
|
+
|
181
|
+
RubyTorrent::BStream.register_bencoded_class self
|
182
|
+
end
|
data/test/test_all.rb
ADDED
data/test/test_client.rb
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
#------------------------------------------------------------------------
|
2
|
+
# Tests for the client
|
3
|
+
#------------------------------------------------------------------------
|
4
|
+
|
5
|
+
require "test/unit"
|
6
|
+
require "rubytorrent/bencoding"
|
7
|
+
require "socket"
|
8
|
+
require "karmasphere/client"
|
9
|
+
require "karmasphere/query"
|
10
|
+
require "karmasphere/response"
|
11
|
+
require "karmasphere/debug"
|
12
|
+
|
13
|
+
# For test purposes, we create a mini-server which bencodes
|
14
|
+
# and transmits this response no matter what input is given.
|
15
|
+
# In this test suite -- we're testing the client -- we
|
16
|
+
# decode the response from the mini-server and match it
|
17
|
+
# against this expected value.
|
18
|
+
|
19
|
+
$stub_response = {
|
20
|
+
"_" => 12345,
|
21
|
+
"facts" => [
|
22
|
+
{
|
23
|
+
"f" => 4000,
|
24
|
+
"v" => -1,
|
25
|
+
"d" => "Invalid Source IP Address (cymru)"
|
26
|
+
}
|
27
|
+
],
|
28
|
+
"combiners" => {
|
29
|
+
"karmasphere.emailchecker" => {
|
30
|
+
"v" => -1000,
|
31
|
+
"d" => "<f4000: if-fail(0) => return bad(1.0)>"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
# We have four test cases in total, exploring each combination of:
|
37
|
+
# * Turnaround vs Stub
|
38
|
+
# * TCP vs UDP
|
39
|
+
|
40
|
+
$port = 8666
|
41
|
+
|
42
|
+
class TC_Client_Turnaround_TCP < Test::Unit::TestCase
|
43
|
+
@@port = $port+1
|
44
|
+
|
45
|
+
def setup
|
46
|
+
@thr=Thread.new do
|
47
|
+
Karmasphere.dprint("TC_Client_Turnaround_TCP: constructing listen server on port #@@port\n");
|
48
|
+
@server = TCPServer.new(nil, @@port)
|
49
|
+
|
50
|
+
# mengwong 20060701: i believe TCPServers are
|
51
|
+
# automatically created with reuseaddr = true,
|
52
|
+
# but you may use the following code to check.
|
53
|
+
Karmasphere.dprint("created server with #{@server.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR).inspect}\n");
|
54
|
+
|
55
|
+
while(session = @server.accept)
|
56
|
+
Thread.new do
|
57
|
+
Karmasphere.dprint("accepted new session #{session.inspect}\n");
|
58
|
+
len_str = session.recv(4)
|
59
|
+
len=len_str.unpack("N")[0]
|
60
|
+
info=session.recv(len)
|
61
|
+
Karmasphere.dprint("received #{len} bytes of info: #{info}\n");
|
62
|
+
session.send len_str+info, 0
|
63
|
+
Karmasphere.dprint("echoing back #{len} bytes of info: #{info}\n");
|
64
|
+
session.close
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def teardown
|
71
|
+
@server.close
|
72
|
+
@server = nil
|
73
|
+
@thr.kill
|
74
|
+
GC.start
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_startup
|
78
|
+
Karmasphere.dprint("test_client: test_startup: beginning\n");
|
79
|
+
|
80
|
+
# we need to wait for the listener thread established in
|
81
|
+
# setup to be ready. we do a poor man's thread
|
82
|
+
# synchronization by simply testing the @server. if we
|
83
|
+
# were doing this right, we'd use a MonitorMixin as
|
84
|
+
# documented on page 138 of the pickax book.
|
85
|
+
while not @server do
|
86
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
87
|
+
Thread.pass
|
88
|
+
end
|
89
|
+
|
90
|
+
Karmasphere.dprint("test_client: test_startup: proceeding.\n");
|
91
|
+
|
92
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
93
|
+
:port => @@port,
|
94
|
+
:type => Karmasphere::CONNECTION_TCP)
|
95
|
+
query = Karmasphere::Query.new({ "_" => 12345 })
|
96
|
+
response = client.ask(query)
|
97
|
+
Karmasphere.dprint("test_client: got back response " , response.instance_variable_get("@hash"));
|
98
|
+
assert_equal query.to_hash, response.instance_variable_get("@hash")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class TC_Client_Turnaround_UDP < Test::Unit::TestCase
|
103
|
+
@@port = $port + 2
|
104
|
+
def setup
|
105
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: constructing listen server on port #@@port\n");
|
106
|
+
@thr=Thread.new do
|
107
|
+
|
108
|
+
@server = UDPSocket.new
|
109
|
+
@server.bind(nil, @@port)
|
110
|
+
|
111
|
+
info=@server.recvfrom(8192)
|
112
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: received ", info, "\n");
|
113
|
+
|
114
|
+
@server.flush
|
115
|
+
client_h=info[1][2]
|
116
|
+
client_p=info[1][1]
|
117
|
+
query=info[0]
|
118
|
+
@server.send query, 0, client_h, client_p
|
119
|
+
@server.flush
|
120
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: sent stuff back to #{client_h}:#{client_p}\n");
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def teardown
|
126
|
+
Karmasphere.dprint("TC_Client_Turnaround_UDP: tearing down server.\n");
|
127
|
+
@server.close
|
128
|
+
@server = nil
|
129
|
+
@thr.kill
|
130
|
+
GC.start
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_startup
|
134
|
+
while not @server do
|
135
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
136
|
+
Thread.pass
|
137
|
+
end
|
138
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
139
|
+
:port => @@port
|
140
|
+
)
|
141
|
+
query = Karmasphere::Query.new({ "_" => 12345 })
|
142
|
+
response = client.ask(query)
|
143
|
+
assert_equal query.to_hash, response.instance_variable_get("@hash")
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_credentials
|
148
|
+
while not @server do
|
149
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
150
|
+
Thread.pass
|
151
|
+
end
|
152
|
+
assert_raises Karmasphere::CredentialsError do
|
153
|
+
Karmasphere::Client.new(:host => "localhost",
|
154
|
+
:port => @@port,
|
155
|
+
:principal => "foo")
|
156
|
+
end
|
157
|
+
assert_raises Karmasphere::CredentialsError do
|
158
|
+
Karmasphere::Client.new(:host => "localhost",
|
159
|
+
:port => @@port,
|
160
|
+
:credentials => "bar")
|
161
|
+
end
|
162
|
+
|
163
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
164
|
+
:port => @@port,
|
165
|
+
:principal => "foo",
|
166
|
+
:credentials => "bar"
|
167
|
+
)
|
168
|
+
query = Karmasphere::Query.new({ "_" => 12345 })
|
169
|
+
response = client.ask(query)
|
170
|
+
assert_equal query.to_hash.merge({"a" => [ "foo", "bar" ]}),
|
171
|
+
response.instance_variable_get("@hash")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class TC_Client_Stub_UDP < Test::Unit::TestCase
|
176
|
+
@@port = $port + 3
|
177
|
+
def setup
|
178
|
+
@thr=Thread.new do
|
179
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: constructing listen server on port #@@port\n");
|
180
|
+
|
181
|
+
@server = UDPSocket.new
|
182
|
+
@server.bind("localhost", @@port)
|
183
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: created server #@port\n");
|
184
|
+
@server_ready = true
|
185
|
+
|
186
|
+
info=@server.recvfrom(8192)
|
187
|
+
@server.flush
|
188
|
+
client_h=info[1][2]
|
189
|
+
client_p=info[1][1]
|
190
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: server received packet from #{client_h}:#{client_p}\n");
|
191
|
+
query=info[0]
|
192
|
+
@server.send $stub_response.to_bencoding, 0, client_h, client_p
|
193
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: miniserver sent stuff back to #{client_h}:#{client_p}\n");
|
194
|
+
Karmasphere.dprint("TC_Client_Stub_UDP: close_write()d.\n");
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def teardown
|
199
|
+
@thr.kill
|
200
|
+
@server.close
|
201
|
+
@server = nil
|
202
|
+
GC.start
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_startup
|
206
|
+
while not @server_ready do
|
207
|
+
Karmasphere.dprint("Stub_UDP test_startup: server not ready. passing.\n");
|
208
|
+
Thread.pass
|
209
|
+
end
|
210
|
+
Karmasphere.dprint("test_startup: proceeding.\n");
|
211
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
212
|
+
:port => @@port
|
213
|
+
)
|
214
|
+
query = Karmasphere::Query.new(:Id => 12345)
|
215
|
+
Karmasphere.dprint("test_startup: client asking query...\n");
|
216
|
+
response = client.ask(query)
|
217
|
+
Karmasphere.dprint("test_startup: ... client received response #{response}\n");
|
218
|
+
assert_equal $stub_response, response.instance_variable_get('@hash')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class TC_Client_Stub_TCP < Test::Unit::TestCase
|
223
|
+
@@port = $port + 4
|
224
|
+
def setup
|
225
|
+
Karmasphere.dprint("TC_Client_Stub_TCP: constructing listen server on port #@@port\n");
|
226
|
+
@thr = Thread.new do
|
227
|
+
|
228
|
+
@server = TCPServer.new(nil, @@port)
|
229
|
+
while(session = @server.accept)
|
230
|
+
Thread.new do
|
231
|
+
while not session.closed?
|
232
|
+
len_str = session.recv(4)
|
233
|
+
len=len_str.unpack("N")[0]
|
234
|
+
info=session.recv(len)
|
235
|
+
response_str=$stub_response.to_bencoding
|
236
|
+
response_len_str = [ response_str.length ].pack "N"
|
237
|
+
session.send response_len_str+response_str, 0
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def teardown
|
245
|
+
@server.close
|
246
|
+
@server = nil
|
247
|
+
@thr.kill
|
248
|
+
GC.start
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_startup
|
252
|
+
while not @server do
|
253
|
+
Karmasphere.dprint("test_startup: server not ready. passing.\n");
|
254
|
+
Thread.pass
|
255
|
+
end
|
256
|
+
assert client=Karmasphere::Client.new(:host => "localhost",
|
257
|
+
:port => @@port,
|
258
|
+
:type => Karmasphere::CONNECTION_TCP)
|
259
|
+
query = Karmasphere::Query.new(:Id => 12345)
|
260
|
+
response = client.ask(query)
|
261
|
+
assert_equal $stub_response, response.instance_variable_get('@hash')
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
data/test/test_query.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "karmasphere/query"
|
3
|
+
|
4
|
+
class TC_Basic_Query < Test::Unit::TestCase
|
5
|
+
def test_automatic_query_id
|
6
|
+
client=Karmasphere::Query.new
|
7
|
+
assert_kind_of Karmasphere::Query, client
|
8
|
+
|
9
|
+
client2=Karmasphere::Query.new
|
10
|
+
assert_kind_of Karmasphere::Query, client2
|
11
|
+
|
12
|
+
assert_equal client.id, "q1"
|
13
|
+
assert_equal client.to_hash, { "_" => "q1" }
|
14
|
+
|
15
|
+
assert_equal client2.id, "q2"
|
16
|
+
assert_equal client2.to_hash, { "_" => "q2" }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_query_identities_munging
|
20
|
+
client=Karmasphere::Query.new(:Id => "q2")
|
21
|
+
assert_kind_of Karmasphere::Query, client
|
22
|
+
|
23
|
+
client.identities=[ ["127.0.0.1", 0 ] ]
|
24
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
25
|
+
assert_equal( { "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
26
|
+
client.to_hash)
|
27
|
+
|
28
|
+
client.identities = [ "127.0.0.1" ]
|
29
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
30
|
+
assert_equal({ "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
31
|
+
client.to_hash)
|
32
|
+
|
33
|
+
client.identities = "127.0.0.1"
|
34
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
35
|
+
assert_equal({ "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
36
|
+
client.to_hash)
|
37
|
+
|
38
|
+
client.identity = "127.0.0.1"
|
39
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities)
|
40
|
+
assert_equal({ "_" => "q2", "i" => [ ["127.0.0.1", 0 ] ] },
|
41
|
+
client.to_hash)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_query_identities_guessing
|
45
|
+
client=Karmasphere::Query.new(:Id => "q3")
|
46
|
+
assert_kind_of Karmasphere::Query, client
|
47
|
+
|
48
|
+
client.identities = "127.0.0.1"
|
49
|
+
assert_equal( [ ["127.0.0.1", 0 ] ], client.identities )
|
50
|
+
assert_equal({ "_" => "q3", "i" => [ ["127.0.0.1", 0 ] ] },
|
51
|
+
client.to_hash)
|
52
|
+
|
53
|
+
client.identities = "::1/128"
|
54
|
+
assert_equal( [ ["::1/128", 1 ] ], client.identities )
|
55
|
+
assert_equal({ "_" => "q3", "i" => [ ["::1/128", 1 ] ] },
|
56
|
+
client.to_hash)
|
57
|
+
|
58
|
+
client.identities = "example.com"
|
59
|
+
assert_equal( [ ["example.com", 2 ] ], client.identities )
|
60
|
+
assert_equal({ "_" => "q3", "i" => [ ["example.com", 2 ] ] },
|
61
|
+
client.to_hash)
|
62
|
+
|
63
|
+
client.identities = "example@example.com"
|
64
|
+
assert_equal( [ ["example@example.com", 3 ] ], client.identities )
|
65
|
+
assert_equal({ "_" => "q3", "i" => [ ["example@example.com", 3 ] ] },
|
66
|
+
client.to_hash)
|
67
|
+
|
68
|
+
client.identities = "http://www.example.com/"
|
69
|
+
assert_equal( [ ["http://www.example.com/", 4 ] ], client.identities )
|
70
|
+
assert_equal({ "_" => "q3", "i" => [ ["http://www.example.com/", 4 ] ] },
|
71
|
+
client.to_hash)
|
72
|
+
|
73
|
+
client.identities = "chunky bacon"
|
74
|
+
assert_equal( [ ["chunky bacon", 5 ] ], client.identities )
|
75
|
+
assert_equal({ "_" => "q3", "i" => [ ["chunky bacon", 5 ] ] },
|
76
|
+
client.to_hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_query_identities_on_construction
|
80
|
+
client=Karmasphere::Query.new(:Id => 'q4',
|
81
|
+
:Identities => "example.com")
|
82
|
+
assert_kind_of Karmasphere::Query, client
|
83
|
+
|
84
|
+
assert_equal( [ ["example.com", 2 ] ], client.identities,
|
85
|
+
"Automatically-detected identity at construction time")
|
86
|
+
assert_equal({ "_" => "q4", "i" => [ ["example.com", 2 ] ] },
|
87
|
+
client.to_hash, "Simple identity at construction time")
|
88
|
+
|
89
|
+
client = Karmasphere::Query.new( :Id => 'q5',
|
90
|
+
:Identities => [ [ "127.0.0.1",
|
91
|
+
Karmasphere::IDT_IP4_ADDRESS,
|
92
|
+
Karmasphere::AUTHENTIC ] ] )
|
93
|
+
assert_equal( [ [ "127.0.0.1", 0, "a" ] ], client.identities )
|
94
|
+
assert_equal({ "_" => "q5", "i" => [ ["127.0.0.1", 0, "a" ] ] },
|
95
|
+
client.to_hash, "Complicated identity at construction time")
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_query_example_query
|
99
|
+
client = Karmasphere::Query.new( :Id => 12345,
|
100
|
+
:Identities => [ [ "127.0.0.1", Karmasphere::IDT_IP4_ADDRESS,
|
101
|
+
Karmasphere::SMTP_CLIENT_IP ],
|
102
|
+
[ "antony@karmasphere.com",
|
103
|
+
Karmasphere::IDT_EMAIL_ADDRESS,
|
104
|
+
Karmasphere::SMTP_ENV_MAIL_FROM ],
|
105
|
+
[ "karmasphere.com",
|
106
|
+
Karmasphere::IDT_DOMAIN_NAME,
|
107
|
+
Karmasphere::SMTP_ENV_HELO ] ],
|
108
|
+
:Composites => "karmasphere.emailchecker",
|
109
|
+
:Flags => 1 )
|
110
|
+
|
111
|
+
assert_kind_of Karmasphere::Query, client
|
112
|
+
|
113
|
+
assert_equal( { "_" => 12345,
|
114
|
+
"i" => [ [ "127.0.0.1", 0, "smtp.client-ip" ],
|
115
|
+
[ "antony@karmasphere.com", 3, "smtp.env.mail-from" ],
|
116
|
+
[ "karmasphere.com", 2, "smtp.env.helo" ] ],
|
117
|
+
"s" => "karmasphere.emailchecker", "fl" => 1 },
|
118
|
+
client.to_hash, "Test query from the protocol spec")
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "karmasphere/response"
|
3
|
+
|
4
|
+
$response_hash = {
|
5
|
+
"_" => 12345,
|
6
|
+
"facts" => [
|
7
|
+
{
|
8
|
+
"f" => 4000,
|
9
|
+
"v" => -1,
|
10
|
+
"d" => "Invalid Source IP Address (cymru)"
|
11
|
+
}
|
12
|
+
],
|
13
|
+
"combiners" => {
|
14
|
+
"karmasphere.emailchecker" => {
|
15
|
+
"v" => -1000,
|
16
|
+
"d" => "<f4000: if-fail(0) => return bad(1.0)>"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
class TC_BasicResponse < Test::Unit::TestCase
|
22
|
+
def test_basicresponse
|
23
|
+
response=nil
|
24
|
+
assert_nothing_raised {
|
25
|
+
response=Karmasphere::Response.new({"_" => 12345})
|
26
|
+
}
|
27
|
+
assert_kind_of Karmasphere::Response, response
|
28
|
+
assert_equal 12345, response.id
|
29
|
+
assert_nil response.time
|
30
|
+
assert_nil response.combinations
|
31
|
+
assert_equal [], response.facts
|
32
|
+
assert_equal [], response.combiner_names
|
33
|
+
assert_nil response.error
|
34
|
+
assert_nil response.message
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_errorresponse
|
38
|
+
response=nil
|
39
|
+
assert_nothing_raised {
|
40
|
+
response=Karmasphere::Response.new({"_" => "q314159",
|
41
|
+
"error" => 1,
|
42
|
+
"message" => "an error occurred"})
|
43
|
+
}
|
44
|
+
assert_kind_of Karmasphere::Response, response
|
45
|
+
assert_equal "q314159", response.id
|
46
|
+
# I don't think I need go through all those assert_nils again
|
47
|
+
assert_equal 1, response.error
|
48
|
+
assert_equal "an error occurred", response.message
|
49
|
+
assert_equal "Response_id 'q314159': 0 combinations, 0 facts\nError: an error occurred\n", response.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_success
|
53
|
+
response=nil
|
54
|
+
assert_nothing_raised {
|
55
|
+
response=Karmasphere::Response.new $response_hash
|
56
|
+
}
|
57
|
+
assert_kind_of Karmasphere::Response, response
|
58
|
+
assert_equal ["karmasphere.emailchecker"], response.combiner_names
|
59
|
+
assert_equal([{
|
60
|
+
"f" => 4000,
|
61
|
+
"v" => -1,
|
62
|
+
"d" => "Invalid Source IP Address (cymru)"
|
63
|
+
}], response.facts)
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: karmasphere-client
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.6.4
|
7
|
+
date: 2007-04-30 00:00:00 -07:00
|
8
|
+
summary: Karmasphere Client
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: devsupport@karmasphere.com
|
12
|
+
homepage: http://www.karmasphere.com.com/devzone/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: karmasphere
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
post_install_message:
|
30
|
+
authors:
|
31
|
+
- Meng Weng Wong
|
32
|
+
files:
|
33
|
+
- bin/rubykarmaclient
|
34
|
+
- test/test_all.rb
|
35
|
+
- test/test_client.rb
|
36
|
+
- test/test_query.rb
|
37
|
+
- test/test_response.rb
|
38
|
+
- lib/karmasphere
|
39
|
+
- lib/karmasphere.rb
|
40
|
+
- lib/rubytorrent
|
41
|
+
- lib/karmasphere/client.rb
|
42
|
+
- lib/karmasphere/constants.rb
|
43
|
+
- lib/karmasphere/debug.rb
|
44
|
+
- lib/karmasphere/query.rb
|
45
|
+
- lib/karmasphere/response.rb
|
46
|
+
- lib/rubytorrent/bencoding.rb
|
47
|
+
- README
|
48
|
+
test_files:
|
49
|
+
- test/test_all.rb
|
50
|
+
rdoc_options: []
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README
|
53
|
+
executables:
|
54
|
+
- rubykarmaclient
|
55
|
+
extensions: []
|
56
|
+
requirements: []
|
57
|
+
dependencies: []
|