Merge pull request #10998 from Homebrew/dependabot/bundler/Library/Homebrew/mechanize-2.8.0

build(deps): bump mechanize from 2.7.7 to 2.8.0 in /Library/Homebrew
This commit is contained in:
Mike McQuaid 2021-04-02 12:11:24 +01:00 committed by GitHub
commit 5c202f3e14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 7700 additions and 115 deletions

View File

@ -7,6 +7,8 @@ GEM
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
ast (2.4.2)
bindata (2.4.8)
bootsnap (1.7.3)
@ -32,20 +34,21 @@ GEM
domain_name (~> 0.5)
i18n (1.8.9)
concurrent-ruby (~> 1.0)
mechanize (2.7.7)
domain_name (~> 0.5, >= 0.5.1)
http-cookie (~> 1.0)
mime-types (>= 1.17.2)
net-http-digest_auth (~> 1.1, >= 1.1.1)
net-http-persistent (>= 2.5.2)
nokogiri (~> 1.6)
ntlm-http (~> 0.1, >= 0.1.1)
mechanize (2.8.0)
addressable (~> 2.7)
domain_name (~> 0.5, >= 0.5.20190701)
http-cookie (~> 1.0, >= 1.0.3)
mime-types (~> 3.0)
net-http-digest_auth (~> 1.4, >= 1.4.1)
net-http-persistent (>= 2.5.2, < 5.0.dev)
nokogiri (~> 1.11, >= 1.11.2)
rubyntlm (~> 0.6, >= 0.6.3)
webrick (~> 1.7)
webrobots (>= 0.0.9, < 0.2)
webrobots (~> 0.1.2)
method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2021.0212)
mime-types-data (3.2021.0225)
mini_portile2 (2.5.0)
minitest (5.14.4)
msgpack (1.4.2)
@ -56,7 +59,6 @@ GEM
nokogiri (1.11.2)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
ntlm-http (0.1.1)
parallel (1.20.1)
parallel_tests (3.6.0)
parallel
@ -73,6 +75,7 @@ GEM
pry (0.14.0)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (4.0.6)
racc (1.5.2)
rack (2.2.3)
rainbow (3.0.0)
@ -133,6 +136,7 @@ GEM
rubocop
ruby-macho (2.5.0)
ruby-progressbar (1.11.0)
rubyntlm (0.6.3)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)

View File

@ -0,0 +1,290 @@
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `addressable` gem.
# Please instead update this file by running `bin/tapioca sync`.
# typed: true
module Addressable
end
module Addressable::IDNA
class << self
def to_ascii(input); end
def to_unicode(input); end
def unicode_normalize_kc(input); end
private
def lookup_unicode_combining_class(codepoint); end
def lookup_unicode_compatibility(codepoint); end
def lookup_unicode_composition(unpacked); end
def lookup_unicode_lowercase(codepoint); end
def punycode_adapt(delta, numpoints, firsttime); end
def punycode_basic?(codepoint); end
def punycode_decode(punycode); end
def punycode_decode_digit(codepoint); end
def punycode_delimiter?(codepoint); end
def punycode_encode(unicode); end
def punycode_encode_digit(d); end
def unicode_compose(unpacked); end
def unicode_compose_pair(ch_one, ch_two); end
def unicode_decompose(unpacked); end
def unicode_decompose_hangul(codepoint); end
def unicode_downcase(input); end
def unicode_sort_canonical(unpacked); end
end
end
Addressable::IDNA::ACE_MAX_LENGTH = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::ACE_PREFIX = T.let(T.unsafe(nil), String)
Addressable::IDNA::COMPOSITION_TABLE = T.let(T.unsafe(nil), Hash)
Addressable::IDNA::HANGUL_LBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_LCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_NCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_SBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_SCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_TBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_TCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_VBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_VCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_BASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_DAMP = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_DELIMITER = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_INITIAL_BIAS = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_INITIAL_N = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_MAXINT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_PRINT_ASCII = T.let(T.unsafe(nil), String)
Addressable::IDNA::PUNYCODE_SKEW = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_TMAX = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_TMIN = T.let(T.unsafe(nil), Integer)
class Addressable::IDNA::PunycodeBadInput < ::StandardError
end
class Addressable::IDNA::PunycodeBigOutput < ::StandardError
end
class Addressable::IDNA::PunycodeOverflow < ::StandardError
end
Addressable::IDNA::UNICODE_DATA = T.let(T.unsafe(nil), Hash)
Addressable::IDNA::UNICODE_DATA_CANONICAL = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_DATA_COMBINING_CLASS = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_DATA_COMPATIBILITY = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_DATA_EXCLUSION = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_DATA_LOWERCASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_DATA_TITLECASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_DATA_UPPERCASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_MAX_LENGTH = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::UNICODE_TABLE = T.let(T.unsafe(nil), String)
Addressable::IDNA::UTF8_REGEX = T.let(T.unsafe(nil), Regexp)
Addressable::IDNA::UTF8_REGEX_MULTIBYTE = T.let(T.unsafe(nil), Regexp)
class Addressable::URI
def initialize(options = T.unsafe(nil)); end
def +(uri); end
def ==(uri); end
def ===(uri); end
def absolute?; end
def authority; end
def authority=(new_authority); end
def basename; end
def default_port; end
def defer_validation; end
def display_uri; end
def domain; end
def dup; end
def empty?; end
def eql?(uri); end
def extname; end
def fragment; end
def fragment=(new_fragment); end
def freeze; end
def hash; end
def host; end
def host=(new_host); end
def hostname; end
def hostname=(new_hostname); end
def inferred_port; end
def inspect; end
def ip_based?; end
def join(uri); end
def join!(uri); end
def merge(hash); end
def merge!(uri); end
def normalize; end
def normalize!; end
def normalized_authority; end
def normalized_fragment; end
def normalized_host; end
def normalized_password; end
def normalized_path; end
def normalized_port; end
def normalized_query(*flags); end
def normalized_scheme; end
def normalized_site; end
def normalized_user; end
def normalized_userinfo; end
def omit(*components); end
def omit!(*components); end
def origin; end
def origin=(new_origin); end
def password; end
def password=(new_password); end
def path; end
def path=(new_path); end
def port; end
def port=(new_port); end
def query; end
def query=(new_query); end
def query_values(return_type = T.unsafe(nil)); end
def query_values=(new_query_values); end
def relative?; end
def request_uri; end
def request_uri=(new_request_uri); end
def route_from(uri); end
def route_to(uri); end
def scheme; end
def scheme=(new_scheme); end
def site; end
def site=(new_site); end
def tld; end
def tld=(new_tld); end
def to_hash; end
def to_s; end
def to_str; end
def user; end
def user=(new_user); end
def userinfo; end
def userinfo=(new_userinfo); end
protected
def remove_composite_values; end
def replace_self(uri); end
def split_path(path); end
def validate; end
class << self
def convert_path(path); end
def encode(uri, return_type = T.unsafe(nil)); end
def encode_component(component, character_class = T.unsafe(nil), upcase_encoded = T.unsafe(nil)); end
def escape(uri, return_type = T.unsafe(nil)); end
def form_encode(form_values, sort = T.unsafe(nil)); end
def form_unencode(encoded_value); end
def heuristic_parse(uri, hints = T.unsafe(nil)); end
def ip_based_schemes; end
def join(*uris); end
def normalize_component(component, character_class = T.unsafe(nil), leave_encoded = T.unsafe(nil)); end
def normalize_path(path); end
def normalized_encode(uri, return_type = T.unsafe(nil)); end
def parse(uri); end
def port_mapping; end
def unencode(uri, return_type = T.unsafe(nil), leave_encoded = T.unsafe(nil)); end
def unencode_component(uri, return_type = T.unsafe(nil), leave_encoded = T.unsafe(nil)); end
def unescape(uri, return_type = T.unsafe(nil), leave_encoded = T.unsafe(nil)); end
def unescape_component(uri, return_type = T.unsafe(nil), leave_encoded = T.unsafe(nil)); end
end
end
module Addressable::URI::CharacterClasses
end
Addressable::URI::CharacterClasses::ALPHA = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::AUTHORITY = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::DIGIT = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::FRAGMENT = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::GEN_DELIMS = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::HOST = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::PATH = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::PCHAR = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::QUERY = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::RESERVED = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::SCHEME = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::SUB_DELIMS = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::UNRESERVED = T.let(T.unsafe(nil), String)
Addressable::URI::EMPTY_STR = T.let(T.unsafe(nil), String)
class Addressable::URI::InvalidURIError < ::StandardError
end
Addressable::URI::NORMPATH = T.let(T.unsafe(nil), Regexp)
Addressable::URI::PARENT = T.let(T.unsafe(nil), String)
Addressable::URI::PORT_MAPPING = T.let(T.unsafe(nil), Hash)
Addressable::URI::RULE_2A = T.let(T.unsafe(nil), Regexp)
Addressable::URI::RULE_2B_2C = T.let(T.unsafe(nil), Regexp)
Addressable::URI::RULE_2D = T.let(T.unsafe(nil), Regexp)
Addressable::URI::RULE_PREFIXED_PARENT = T.let(T.unsafe(nil), Regexp)
Addressable::URI::SELF_REF = T.let(T.unsafe(nil), String)
Addressable::URI::SEQUENCE_ENCODING_TABLE = T.let(T.unsafe(nil), Hash)
Addressable::URI::SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = T.let(T.unsafe(nil), Hash)
Addressable::URI::SLASH = T.let(T.unsafe(nil), String)
Addressable::URI::URIREGEX = T.let(T.unsafe(nil), Regexp)
module Addressable::VERSION
end
Addressable::VERSION::MAJOR = T.let(T.unsafe(nil), Integer)
Addressable::VERSION::MINOR = T.let(T.unsafe(nil), Integer)
Addressable::VERSION::STRING = T.let(T.unsafe(nil), String)
Addressable::VERSION::TINY = T.let(T.unsafe(nil), Integer)

View File

@ -1,6 +1,6 @@
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `mechanize` gem.
# Please instead update this file by running `tapioca sync`.
# Please instead update this file by running `bin/tapioca sync`.
# typed: true
@ -293,6 +293,7 @@ class Mechanize::FileResponse
def content_length; end
def each; end
def each_header; end
def file_path; end
def get_fields(key); end
def http_version; end
def message; end
@ -699,6 +700,10 @@ class Mechanize::HTTP::Agent
def webrobots; end
end
Mechanize::HTTP::Agent::CREDENTIAL_HEADERS = T.let(T.unsafe(nil), Array)
Mechanize::HTTP::Agent::POST_HEADERS = T.let(T.unsafe(nil), Array)
Mechanize::HTTP::Agent::RobotsKey = T.let(T.unsafe(nil), Symbol)
class Mechanize::HTTP::AuthChallenge < ::Struct

View File

@ -1,6 +1,6 @@
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `mime-types-data` gem.
# Please instead update this file by running `tapioca sync`.
# Please instead update this file by running `bin/tapioca sync`.
# typed: true

View File

@ -0,0 +1,152 @@
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `public_suffix` gem.
# Please instead update this file by running `bin/tapioca sync`.
# typed: true
module PublicSuffix
class << self
def decompose(name, rule); end
def domain(name, **options); end
def normalize(name); end
def parse(name, list: T.unsafe(nil), default_rule: T.unsafe(nil), ignore_private: T.unsafe(nil)); end
def valid?(name, list: T.unsafe(nil), default_rule: T.unsafe(nil), ignore_private: T.unsafe(nil)); end
end
end
PublicSuffix::BANG = T.let(T.unsafe(nil), String)
PublicSuffix::DOT = T.let(T.unsafe(nil), String)
class PublicSuffix::Domain
def initialize(*args); end
def domain; end
def domain?; end
def name; end
def sld; end
def subdomain; end
def subdomain?; end
def tld; end
def to_a; end
def to_s; end
def trd; end
class << self
def name_to_labels(name); end
end
end
class PublicSuffix::DomainInvalid < ::PublicSuffix::Error
end
class PublicSuffix::DomainNotAllowed < ::PublicSuffix::DomainInvalid
end
class PublicSuffix::Error < ::StandardError
end
class PublicSuffix::List
def initialize; end
def <<(rule); end
def ==(other); end
def add(rule); end
def clear; end
def default_rule; end
def each(&block); end
def empty?; end
def eql?(other); end
def find(name, default: T.unsafe(nil), **options); end
def size; end
protected
def rules; end
private
def entry_to_rule(entry, value); end
def rule_to_entry(rule); end
def select(name, ignore_private: T.unsafe(nil)); end
class << self
def default(**options); end
def default=(value); end
def parse(input, private_domains: T.unsafe(nil)); end
end
end
PublicSuffix::List::DEFAULT_LIST_PATH = T.let(T.unsafe(nil), String)
module PublicSuffix::Rule
class << self
def default; end
def factory(content, private: T.unsafe(nil)); end
end
end
class PublicSuffix::Rule::Base
def initialize(value:, length: T.unsafe(nil), private: T.unsafe(nil)); end
def ==(other); end
def decompose(*_arg0); end
def eql?(other); end
def length; end
def match?(name); end
def parts; end
def private; end
def value; end
class << self
def build(content, private: T.unsafe(nil)); end
end
end
class PublicSuffix::Rule::Entry < ::Struct
def length; end
def length=(_); end
def private; end
def private=(_); end
def type; end
def type=(_); end
class << self
def [](*_arg0); end
def inspect; end
def members; end
def new(*_arg0); end
end
end
class PublicSuffix::Rule::Exception < ::PublicSuffix::Rule::Base
def decompose(domain); end
def parts; end
def rule; end
class << self
def build(content, private: T.unsafe(nil)); end
end
end
class PublicSuffix::Rule::Normal < ::PublicSuffix::Rule::Base
def decompose(domain); end
def parts; end
def rule; end
end
class PublicSuffix::Rule::Wildcard < ::PublicSuffix::Rule::Base
def initialize(value:, length: T.unsafe(nil), private: T.unsafe(nil)); end
def decompose(domain); end
def parts; end
def rule; end
class << self
def build(content, private: T.unsafe(nil)); end
end
end
PublicSuffix::STAR = T.let(T.unsafe(nil), String)
PublicSuffix::VERSION = T.let(T.unsafe(nil), String)

View File

@ -1,15 +1,14 @@
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `ntlm-http` gem.
# Please instead update this file by running `tapioca generate --exclude json`.
# This is an autogenerated file for types exported from the `rubyntlm` gem.
# Please instead update this file by running `bin/tapioca sync`.
# typed: true
module Net::NTLM
class << self
def apply_des(plain, keys); end
def decode_utf16le(str); end
def encode_utf16le(str); end
def gen_keys(str); end
def is_ntlm_hash?(data); end
def lm_hash(password); end
def lm_response(arg); end
def lmv2_response(arg, opt = T.unsafe(nil)); end
@ -20,7 +19,6 @@ module Net::NTLM
def ntlmv2_response(arg, opt = T.unsafe(nil)); end
def pack_int64le(val); end
def split7(str); end
def swap16(str); end
end
end
@ -29,6 +27,7 @@ class Net::NTLM::Blob < ::Net::NTLM::FieldSet
def blob_signature=(val); end
def challenge; end
def challenge=(val); end
def parse(str, offset = T.unsafe(nil)); end
def reserved; end
def reserved=(val); end
def target_info; end
@ -39,9 +38,91 @@ class Net::NTLM::Blob < ::Net::NTLM::FieldSet
def unknown1=(val); end
def unknown2; end
def unknown2=(val); end
end
class Net::NTLM::ChannelBinding
def initialize(outer_channel); end
def acceptor_address_length; end
def acceptor_addrtype; end
def application_data; end
def channel; end
def channel_binding_token; end
def channel_hash; end
def gss_channel_bindings_struct; end
def initiator_address_length; end
def initiator_addtype; end
def unique_prefix; end
class << self
def inherited(subclass); end
def create(outer_channel); end
end
end
class Net::NTLM::Client
def initialize(username, password, opts = T.unsafe(nil)); end
def domain; end
def flags; end
def init_context(resp = T.unsafe(nil), channel_binding = T.unsafe(nil)); end
def password; end
def session; end
def session_key; end
def username; end
def workstation; end
private
def type1_message; end
end
class Net::NTLM::Client::Session
def initialize(client, challenge_message, channel_binding = T.unsafe(nil)); end
def authenticate!; end
def challenge_message; end
def channel_binding; end
def client; end
def exported_session_key; end
def seal_message(message); end
def sign_message(message); end
def unseal_message(emessage); end
def verify_signature(signature, message); end
private
def blob; end
def calculate_user_session_key!; end
def client_challenge; end
def client_cipher; end
def client_seal_key; end
def client_sign_key; end
def domain; end
def lmv2_resp; end
def negotiate_key_exchange?; end
def nt_proof_str; end
def ntlmv2_hash; end
def ntlmv2_resp; end
def oem_or_unicode_str(str); end
def password; end
def raw_sequence; end
def sequence; end
def server_challenge; end
def server_cipher; end
def server_seal_key; end
def server_sign_key; end
def target_info; end
def timestamp; end
def use_oem_strings?; end
def user_session_key; end
def username; end
def workstation; end
end
class Net::NTLM::EncodeUtil
class << self
def decode_utf16le(str); end
def encode_utf16le(str); end
end
end
@ -49,10 +130,12 @@ class Net::NTLM::Field
def initialize(opts); end
def active; end
def active=(_); end
def active=(_arg0); end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
def size; end
def value; end
def value=(_); end
def value=(_arg0); end
end
class Net::NTLM::FieldSet
@ -62,12 +145,12 @@ class Net::NTLM::FieldSet
def []=(name, val); end
def disable(name); end
def enable(name); end
def has_disabled_fields?; end
def parse(str, offset = T.unsafe(nil)); end
def serialize; end
def size; end
class << self
def define(&block); end
def int16LE(name, opts); end
def int32LE(name, opts); end
def int64LE(name, opts); end
@ -106,23 +189,27 @@ class Net::NTLM::Int64LE < ::Net::NTLM::Field
def serialize; end
end
class Net::NTLM::InvalidTargetDataError < ::Net::NTLM::NtlmError
def initialize(msg, data); end
def data; end
end
class Net::NTLM::Message < ::Net::NTLM::FieldSet
def data_edge; end
def data_size; end
def decode64(str); end
def deflag; end
def dump_flags; end
def encode64; end
def has_flag?(flag); end
def head_size; end
def parse(str); end
def security_buffers; end
def serialize; end
def set_flag(flag); end
def size; end
private
def data_edge; end
def deflag; end
def security_buffers; end
class << self
def decode64(str); end
def parse(str); end
@ -134,10 +221,6 @@ class Net::NTLM::Message::Type0 < ::Net::NTLM::Message
def sign=(val); end
def type; end
def type=(val); end
class << self
def inherited(subclass); end
end
end
class Net::NTLM::Message::Type1 < ::Net::NTLM::Message
@ -145,20 +228,14 @@ class Net::NTLM::Message::Type1 < ::Net::NTLM::Message
def domain=(val); end
def flag; end
def flag=(val); end
def padding; end
def padding=(val); end
def parse(str); end
def os_version; end
def os_version=(val); end
def sign; end
def sign=(val); end
def type; end
def type=(val); end
def workstation; end
def workstation=(val); end
class << self
def inherited(subclass); end
def parse(str); end
end
end
class Net::NTLM::Message::Type2 < ::Net::NTLM::Message
@ -168,9 +245,8 @@ class Net::NTLM::Message::Type2 < ::Net::NTLM::Message
def context=(val); end
def flag; end
def flag=(val); end
def padding; end
def padding=(val); end
def parse(str); end
def os_version; end
def os_version=(val); end
def response(arg, opt = T.unsafe(nil)); end
def sign; end
def sign=(val); end
@ -180,14 +256,10 @@ class Net::NTLM::Message::Type2 < ::Net::NTLM::Message
def target_name=(val); end
def type; end
def type=(val); end
class << self
def inherited(subclass); end
def parse(str); end
end
end
class Net::NTLM::Message::Type3 < ::Net::NTLM::Message
def blank_password?(server_challenge); end
def domain; end
def domain=(val); end
def flag; end
@ -196,6 +268,10 @@ class Net::NTLM::Message::Type3 < ::Net::NTLM::Message
def lm_response=(val); end
def ntlm_response; end
def ntlm_response=(val); end
def ntlm_version; end
def os_version; end
def os_version=(val); end
def password?(password, server_challenge); end
def session_key; end
def session_key=(val); end
def sign; end
@ -207,18 +283,21 @@ class Net::NTLM::Message::Type3 < ::Net::NTLM::Message
def workstation; end
def workstation=(val); end
private
def ntlm2_session_password?(password, server_challenge); end
def ntlmv2_password?(password, server_challenge); end
class << self
def create(arg, opt = T.unsafe(nil)); end
def inherited(subclass); end
def parse(str); end
end
end
class Net::NTLM::SecurityBuffer < ::Net::NTLM::FieldSet
def initialize(opts); end
def initialize(opts = T.unsafe(nil)); end
def active; end
def active=(_); end
def active=(_arg0); end
def allocated; end
def allocated=(val); end
def data_size; end
@ -230,10 +309,6 @@ class Net::NTLM::SecurityBuffer < ::Net::NTLM::FieldSet
def serialize; end
def value; end
def value=(val); end
class << self
def inherited(subclass); end
end
end
class Net::NTLM::String < ::Net::NTLM::Field
@ -244,13 +319,14 @@ class Net::NTLM::String < ::Net::NTLM::Field
def value=(val); end
end
module Net::NTLM::VERSION
class Net::NTLM::TargetInfo
def initialize(av_pair_sequence); end
def av_pairs; end
def to_s; end
private
def read_pairs(av_pair_sequence); end
def to_hex(str); end
end
Net::NTLM::VERSION::MAJOR = T.let(T.unsafe(nil), Integer)
Net::NTLM::VERSION::MINOR = T.let(T.unsafe(nil), Integer)
Net::NTLM::VERSION::STRING = T.let(T.unsafe(nil), String)
Net::NTLM::VERSION::TINY = T.let(T.unsafe(nil), Integer)

View File

@ -2685,6 +2685,104 @@ module ActiveSupport
def self.version(); end
end
class Addressable::Template
def ==(template); end
def eql?(template); end
def expand(mapping, processor=T.unsafe(nil), normalize_values=T.unsafe(nil)); end
def extract(uri, processor=T.unsafe(nil)); end
def generate(params=T.unsafe(nil), recall=T.unsafe(nil), options=T.unsafe(nil)); end
def initialize(pattern); end
def keys(); end
def match(uri, processor=T.unsafe(nil)); end
def named_captures(); end
def names(); end
def partial_expand(mapping, processor=T.unsafe(nil), normalize_values=T.unsafe(nil)); end
def pattern(); end
def source(); end
def to_regexp(); end
def variable_defaults(); end
def variables(); end
EXPRESSION = ::T.let(nil, ::T.untyped)
JOINERS = ::T.let(nil, ::T.untyped)
LEADERS = ::T.let(nil, ::T.untyped)
RESERVED = ::T.let(nil, ::T.untyped)
UNRESERVED = ::T.let(nil, ::T.untyped)
VARIABLE_LIST = ::T.let(nil, ::T.untyped)
VARNAME = ::T.let(nil, ::T.untyped)
VARSPEC = ::T.let(nil, ::T.untyped)
end
class Addressable::Template::InvalidTemplateOperatorError
end
class Addressable::Template::InvalidTemplateOperatorError
end
class Addressable::Template::InvalidTemplateValueError
end
class Addressable::Template::InvalidTemplateValueError
end
class Addressable::Template::MatchData
def [](key, len=T.unsafe(nil)); end
def captures(); end
def initialize(uri, template, mapping); end
def keys(); end
def mapping(); end
def names(); end
def post_match(); end
def pre_match(); end
def string(); end
def template(); end
def to_a(); end
def uri(); end
def values(); end
def values_at(*indexes); end
def variables(); end
end
class Addressable::Template::MatchData
end
class Addressable::Template::TemplateOperatorAbortedError
end
class Addressable::Template::TemplateOperatorAbortedError
end
class Addressable::Template
end
class Addrinfo
def connect_internal(local_addrinfo, timeout=T.unsafe(nil)); end
end
@ -9630,12 +9728,6 @@ JSON::State = JSON::Ext::Generator::State
JSON::UnparserError = JSON::GeneratorError
module Kconv
AUTO = ::T.let(nil, ::T.untyped)
NOCONV = ::T.let(nil, ::T.untyped)
UNKNOWN = ::T.let(nil, ::T.untyped)
end
class Keg::ConflictError
extend ::T::Private::Methods::MethodHooks
extend ::T::Private::Methods::SingletonMethodHooks
@ -12349,13 +12441,9 @@ end
Net::HTTPFatalErrorCode = Net::HTTPClientError
class Net::HTTPInformation
end
Net::HTTPInformation::EXCEPTION_TYPE = Net::HTTPError
Net::HTTPInformationCode::EXCEPTION_TYPE = Net::HTTPError
class Net::HTTPInformation
end
Net::HTTPInformationCode = Net::HTTPInformation
class Net::HTTPLoopDetected
HAS_BODY = ::T.let(nil, ::T.untyped)
@ -12419,13 +12507,9 @@ Net::HTTPServerErrorCode = Net::HTTPServerError
Net::HTTPSession = Net::HTTP
class Net::HTTPSuccess
end
Net::HTTPSuccess::EXCEPTION_TYPE = Net::HTTPError
Net::HTTPSuccessCode::EXCEPTION_TYPE = Net::HTTPError
class Net::HTTPSuccess
end
Net::HTTPSuccessCode = Net::HTTPSuccess
class Net::HTTPURITooLong
HAS_BODY = ::T.let(nil, ::T.untyped)
@ -29167,18 +29251,8 @@ class String
def indent!(amount, indent_string=T.unsafe(nil), indent_empty_lines=T.unsafe(nil)); end
def iseuc(); end
def isjis(); end
def issjis(); end
def isutf8(); end
def italic(); end
def kconv(to_enc, from_enc=T.unsafe(nil)); end
def light_black(); end
def light_blue(); end
@ -29259,20 +29333,6 @@ class String
def to_time(form=T.unsafe(nil)); end
def toeuc(); end
def tojis(); end
def tolocale(); end
def tosjis(); end
def toutf16(); end
def toutf32(); end
def toutf8(); end
def truncate(truncate_at, options=T.unsafe(nil)); end
def truncate_bytes(truncate_at, omission: T.unsafe(nil)); end

View File

@ -9,6 +9,8 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.14.4/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-2.0.4/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.4.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.1.3.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/public_suffix-4.0.6/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/addressable-2.7.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ast-2.4.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/bindata-2.4.8/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/x86_64-darwin-14/2.6.0-static/msgpack-1.4.2"
@ -37,7 +39,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/elftools-1.1.3/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/x86_64-darwin-14/2.6.0-static/hpricot-0.8.6"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/hpricot-0.8.6/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/http-cookie-1.0.3/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mime-types-data-3.2021.0212/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mime-types-data-3.2021.0225/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mime-types-3.3.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/net-http-digest_auth-1.4.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/net-http-persistent-4.0.1/lib"
@ -45,10 +47,10 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mini_portile2-2.5.0/l
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/x86_64-darwin-14/2.6.0-static/racc-1.5.2"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/racc-1.5.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/nokogiri-1.11.2-x86_64-darwin/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ntlm-http-0.1.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubyntlm-0.6.3/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/webrick-1.7.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/webrobots-0.1.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mechanize-2.7.7/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mechanize-2.8.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/method_source-1.0.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mustache-1.1.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.20.1/lib"

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
require 'addressable/uri'
require 'addressable/template'

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
# encoding:utf-8
#--
# Copyright (C) Bob Aman
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++
begin
require "addressable/idna/native"
rescue LoadError
# libidn or the idn gem was not available, fall back on a pure-Ruby
# implementation...
require "addressable/idna/pure"
end

View File

@ -0,0 +1,61 @@
# frozen_string_literal: true
# encoding:utf-8
#--
# Copyright (C) Bob Aman
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++
require "idn"
module Addressable
module IDNA
def self.punycode_encode(value)
IDN::Punycode.encode(value.to_s)
end
def self.punycode_decode(value)
IDN::Punycode.decode(value.to_s)
end
def self.unicode_normalize_kc(value)
IDN::Stringprep.nfkc_normalize(value.to_s)
end
def self.to_ascii(value)
value.to_s.split('.', -1).map do |segment|
if segment.size > 0 && segment.size < 64
IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED)
elsif segment.size >= 64
segment
else
''
end
end.join('.')
end
def self.to_unicode(value)
value.to_s.split('.', -1).map do |segment|
if segment.size > 0 && segment.size < 64
IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED)
elsif segment.size >= 64
segment
else
''
end
end.join('.')
end
end
end

View File

@ -0,0 +1,676 @@
# frozen_string_literal: true
# encoding:utf-8
#--
# Copyright (C) Bob Aman
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++
module Addressable
module IDNA
# This module is loosely based on idn_actionmailer by Mick Staugaard,
# the unicode library by Yoshida Masato, and the punycode implementation
# by Kazuhiro Nishiyama. Most of the code was copied verbatim, but
# some reformatting was done, and some translation from C was done.
#
# Without their code to work from as a base, we'd all still be relying
# on the presence of libidn. Which nobody ever seems to have installed.
#
# Original sources:
# http://github.com/staugaard/idn_actionmailer
# http://www.yoshidam.net/Ruby.html#unicode
# http://rubyforge.org/frs/?group_id=2550
UNICODE_TABLE = File.expand_path(
File.join(File.dirname(__FILE__), '../../..', 'data/unicode.data')
)
ACE_PREFIX = "xn--"
UTF8_REGEX = /\A(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*\z/mnx
UTF8_REGEX_MULTIBYTE = /(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)/mnx
# :startdoc:
# Converts from a Unicode internationalized domain name to an ASCII
# domain name as described in RFC 3490.
def self.to_ascii(input)
input = input.to_s unless input.is_a?(String)
input = input.dup
if input.respond_to?(:force_encoding)
input.force_encoding(Encoding::ASCII_8BIT)
end
if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE
parts = unicode_downcase(input).split('.')
parts.map! do |part|
if part.respond_to?(:force_encoding)
part.force_encoding(Encoding::ASCII_8BIT)
end
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
else
part
end
end
parts.join('.')
else
input
end
end
# Converts from an ASCII domain name to a Unicode internationalized
# domain name as described in RFC 3490.
def self.to_unicode(input)
input = input.to_s unless input.is_a?(String)
parts = input.split('.')
parts.map! do |part|
if part =~ /^#{ACE_PREFIX}(.+)/
begin
punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
rescue Addressable::IDNA::PunycodeBadInput
# toUnicode is explicitly defined as never-fails by the spec
part
end
else
part
end
end
output = parts.join('.')
if output.respond_to?(:force_encoding)
output.force_encoding(Encoding::UTF_8)
end
output
end
# Unicode normalization form KC.
def self.unicode_normalize_kc(input)
input = input.to_s unless input.is_a?(String)
unpacked = input.unpack("U*")
unpacked =
unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
return unpacked.pack("U*")
end
##
# Unicode aware downcase method.
#
# @api private
# @param [String] input
# The input string.
# @return [String] The downcased result.
def self.unicode_downcase(input)
input = input.to_s unless input.is_a?(String)
unpacked = input.unpack("U*")
unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
return unpacked.pack("U*")
end
private_class_method :unicode_downcase
def self.unicode_compose(unpacked)
unpacked_result = []
length = unpacked.length
return unpacked if length == 0
starter = unpacked[0]
starter_cc = lookup_unicode_combining_class(starter)
starter_cc = 256 if starter_cc != 0
for i in 1...length
ch = unpacked[i]
if (starter_cc == 0 &&
(composite = unicode_compose_pair(starter, ch)) != nil)
starter = composite
else
unpacked_result << starter
starter = ch
end
end
unpacked_result << starter
return unpacked_result
end
private_class_method :unicode_compose
def self.unicode_compose_pair(ch_one, ch_two)
if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
# Hangul L + V
return HANGUL_SBASE + (
(ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
) * HANGUL_TCOUNT
elsif ch_one >= HANGUL_SBASE &&
ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
(ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
# Hangul LV + T
return ch_one + (ch_two - HANGUL_TBASE)
end
p = []
ucs4_to_utf8 = lambda do |ch|
if ch < 128
p << ch
elsif ch < 2048
p << (ch >> 6 | 192)
p << (ch & 63 | 128)
elsif ch < 0x10000
p << (ch >> 12 | 224)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
elsif ch < 0x200000
p << (ch >> 18 | 240)
p << (ch >> 12 & 63 | 128)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
elsif ch < 0x4000000
p << (ch >> 24 | 248)
p << (ch >> 18 & 63 | 128)
p << (ch >> 12 & 63 | 128)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
elsif ch < 0x80000000
p << (ch >> 30 | 252)
p << (ch >> 24 & 63 | 128)
p << (ch >> 18 & 63 | 128)
p << (ch >> 12 & 63 | 128)
p << (ch >> 6 & 63 | 128)
p << (ch & 63 | 128)
end
end
ucs4_to_utf8.call(ch_one)
ucs4_to_utf8.call(ch_two)
return lookup_unicode_composition(p)
end
private_class_method :unicode_compose_pair
def self.unicode_sort_canonical(unpacked)
unpacked = unpacked.dup
i = 1
length = unpacked.length
return unpacked if length < 2
while i < length
last = unpacked[i-1]
ch = unpacked[i]
last_cc = lookup_unicode_combining_class(last)
cc = lookup_unicode_combining_class(ch)
if cc != 0 && last_cc != 0 && last_cc > cc
unpacked[i] = last
unpacked[i-1] = ch
i -= 1 if i > 1
else
i += 1
end
end
return unpacked
end
private_class_method :unicode_sort_canonical
def self.unicode_decompose(unpacked)
unpacked_result = []
for cp in unpacked
if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
l, v, t = unicode_decompose_hangul(cp)
unpacked_result << l
unpacked_result << v if v
unpacked_result << t if t
else
dc = lookup_unicode_compatibility(cp)
unless dc
unpacked_result << cp
else
unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
end
end
end
return unpacked_result
end
private_class_method :unicode_decompose
def self.unicode_decompose_hangul(codepoint)
sindex = codepoint - HANGUL_SBASE;
if sindex < 0 || sindex >= HANGUL_SCOUNT
l = codepoint
v = t = nil
return l, v, t
end
l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
if t == HANGUL_TBASE
t = nil
end
return l, v, t
end
private_class_method :unicode_decompose_hangul
def self.lookup_unicode_combining_class(codepoint)
codepoint_data = UNICODE_DATA[codepoint]
(codepoint_data ?
(codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
0)
end
private_class_method :lookup_unicode_combining_class
def self.lookup_unicode_compatibility(codepoint)
codepoint_data = UNICODE_DATA[codepoint]
(codepoint_data ?
codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
end
private_class_method :lookup_unicode_compatibility
def self.lookup_unicode_lowercase(codepoint)
codepoint_data = UNICODE_DATA[codepoint]
(codepoint_data ?
(codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
codepoint)
end
private_class_method :lookup_unicode_lowercase
def self.lookup_unicode_composition(unpacked)
return COMPOSITION_TABLE[unpacked]
end
private_class_method :lookup_unicode_composition
HANGUL_SBASE = 0xac00
HANGUL_LBASE = 0x1100
HANGUL_LCOUNT = 19
HANGUL_VBASE = 0x1161
HANGUL_VCOUNT = 21
HANGUL_TBASE = 0x11a7
HANGUL_TCOUNT = 28
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
UNICODE_DATA_COMBINING_CLASS = 0
UNICODE_DATA_EXCLUSION = 1
UNICODE_DATA_CANONICAL = 2
UNICODE_DATA_COMPATIBILITY = 3
UNICODE_DATA_UPPERCASE = 4
UNICODE_DATA_LOWERCASE = 5
UNICODE_DATA_TITLECASE = 6
begin
if defined?(FakeFS)
fakefs_state = FakeFS.activated?
FakeFS.deactivate!
end
# This is a sparse Unicode table. Codepoints without entries are
# assumed to have the value: [0, 0, nil, nil, nil, nil, nil]
UNICODE_DATA = File.open(UNICODE_TABLE, "rb") do |file|
Marshal.load(file.read)
end
ensure
if defined?(FakeFS)
FakeFS.activate! if fakefs_state
end
end
COMPOSITION_TABLE = {}
UNICODE_DATA.each do |codepoint, data|
canonical = data[UNICODE_DATA_CANONICAL]
exclusion = data[UNICODE_DATA_EXCLUSION]
if canonical && exclusion == 0
COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint
end
end
UNICODE_MAX_LENGTH = 256
ACE_MAX_LENGTH = 256
PUNYCODE_BASE = 36
PUNYCODE_TMIN = 1
PUNYCODE_TMAX = 26
PUNYCODE_SKEW = 38
PUNYCODE_DAMP = 700
PUNYCODE_INITIAL_BIAS = 72
PUNYCODE_INITIAL_N = 0x80
PUNYCODE_DELIMITER = 0x2D
PUNYCODE_MAXINT = 1 << 64
PUNYCODE_PRINT_ASCII =
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
" !\"\#$%&'()*+,-./" +
"0123456789:;<=>?" +
"@ABCDEFGHIJKLMNO" +
"PQRSTUVWXYZ[\\]^_" +
"`abcdefghijklmno" +
"pqrstuvwxyz{|}~\n"
# Input is invalid.
class PunycodeBadInput < StandardError; end
# Output would exceed the space provided.
class PunycodeBigOutput < StandardError; end
# Input needs wider integers to process.
class PunycodeOverflow < StandardError; end
def self.punycode_encode(unicode)
unicode = unicode.to_s unless unicode.is_a?(String)
input = unicode.unpack("U*")
output = [0] * (ACE_MAX_LENGTH + 1)
input_length = input.size
output_length = [ACE_MAX_LENGTH]
# Initialize the state
n = PUNYCODE_INITIAL_N
delta = out = 0
max_out = output_length[0]
bias = PUNYCODE_INITIAL_BIAS
# Handle the basic code points:
input_length.times do |j|
if punycode_basic?(input[j])
if max_out - out < 2
raise PunycodeBigOutput,
"Output would exceed the space provided."
end
output[out] = input[j]
out += 1
end
end
h = b = out
# h is the number of code points that have been handled, b is the
# number of basic code points, and out is the number of characters
# that have been output.
if b > 0
output[out] = PUNYCODE_DELIMITER
out += 1
end
# Main encoding loop:
while h < input_length
# All non-basic code points < n have been
# handled already. Find the next larger one:
m = PUNYCODE_MAXINT
input_length.times do |j|
m = input[j] if (n...m) === input[j]
end
# Increase delta enough to advance the decoder's
# <n,i> state to <m,0>, but guard against overflow:
if m - n > (PUNYCODE_MAXINT - delta) / (h + 1)
raise PunycodeOverflow, "Input needs wider integers to process."
end
delta += (m - n) * (h + 1)
n = m
input_length.times do |j|
# Punycode does not need to check whether input[j] is basic:
if input[j] < n
delta += 1
if delta == 0
raise PunycodeOverflow,
"Input needs wider integers to process."
end
end
if input[j] == n
# Represent delta as a generalized variable-length integer:
q = delta; k = PUNYCODE_BASE
while true
if out >= max_out
raise PunycodeBigOutput,
"Output would exceed the space provided."
end
t = (
if k <= bias
PUNYCODE_TMIN
elsif k >= bias + PUNYCODE_TMAX
PUNYCODE_TMAX
else
k - bias
end
)
break if q < t
output[out] =
punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t))
out += 1
q = (q - t) / (PUNYCODE_BASE - t)
k += PUNYCODE_BASE
end
output[out] = punycode_encode_digit(q)
out += 1
bias = punycode_adapt(delta, h + 1, h == b)
delta = 0
h += 1
end
end
delta += 1
n += 1
end
output_length[0] = out
outlen = out
outlen.times do |j|
c = output[j]
unless c >= 0 && c <= 127
raise StandardError, "Invalid output char."
end
unless PUNYCODE_PRINT_ASCII[c]
raise PunycodeBadInput, "Input is invalid."
end
end
output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
end
private_class_method :punycode_encode
def self.punycode_decode(punycode)
input = []
output = []
if ACE_MAX_LENGTH * 2 < punycode.size
raise PunycodeBigOutput, "Output would exceed the space provided."
end
punycode.each_byte do |c|
unless c >= 0 && c <= 127
raise PunycodeBadInput, "Input is invalid."
end
input.push(c)
end
input_length = input.length
output_length = [UNICODE_MAX_LENGTH]
# Initialize the state
n = PUNYCODE_INITIAL_N
out = i = 0
max_out = output_length[0]
bias = PUNYCODE_INITIAL_BIAS
# Handle the basic code points: Let b be the number of input code
# points before the last delimiter, or 0 if there is none, then
# copy the first b code points to the output.
b = 0
input_length.times do |j|
b = j if punycode_delimiter?(input[j])
end
if b > max_out
raise PunycodeBigOutput, "Output would exceed the space provided."
end
b.times do |j|
unless punycode_basic?(input[j])
raise PunycodeBadInput, "Input is invalid."
end
output[out] = input[j]
out+=1
end
# Main decoding loop: Start just after the last delimiter if any
# basic code points were copied; start at the beginning otherwise.
in_ = b > 0 ? b + 1 : 0
while in_ < input_length
# in_ is the index of the next character to be consumed, and
# out is the number of code points in the output array.
# Decode a generalized variable-length integer into delta,
# which gets added to i. The overflow checking is easier
# if we increase i as we go, then subtract off its starting
# value at the end to obtain delta.
oldi = i; w = 1; k = PUNYCODE_BASE
while true
if in_ >= input_length
raise PunycodeBadInput, "Input is invalid."
end
digit = punycode_decode_digit(input[in_])
in_+=1
if digit >= PUNYCODE_BASE
raise PunycodeBadInput, "Input is invalid."
end
if digit > (PUNYCODE_MAXINT - i) / w
raise PunycodeOverflow, "Input needs wider integers to process."
end
i += digit * w
t = (
if k <= bias
PUNYCODE_TMIN
elsif k >= bias + PUNYCODE_TMAX
PUNYCODE_TMAX
else
k - bias
end
)
break if digit < t
if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t)
raise PunycodeOverflow, "Input needs wider integers to process."
end
w *= PUNYCODE_BASE - t
k += PUNYCODE_BASE
end
bias = punycode_adapt(i - oldi, out + 1, oldi == 0)
# I was supposed to wrap around from out + 1 to 0,
# incrementing n each time, so we'll fix that now:
if i / (out + 1) > PUNYCODE_MAXINT - n
raise PunycodeOverflow, "Input needs wider integers to process."
end
n += i / (out + 1)
i %= out + 1
# Insert n at position i of the output:
# not needed for Punycode:
# raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base
if out >= max_out
raise PunycodeBigOutput, "Output would exceed the space provided."
end
#memmove(output + i + 1, output + i, (out - i) * sizeof *output)
output[i + 1, out - i] = output[i, out - i]
output[i] = n
i += 1
out += 1
end
output_length[0] = out
output.pack("U*")
end
private_class_method :punycode_decode
def self.punycode_basic?(codepoint)
codepoint < 0x80
end
private_class_method :punycode_basic?
def self.punycode_delimiter?(codepoint)
codepoint == PUNYCODE_DELIMITER
end
private_class_method :punycode_delimiter?
def self.punycode_encode_digit(d)
d + 22 + 75 * ((d < 26) ? 1 : 0)
end
private_class_method :punycode_encode_digit
# Returns the numeric value of a basic codepoint
# (for use in representing integers) in the range 0 to
# base - 1, or PUNYCODE_BASE if codepoint does not represent a value.
def self.punycode_decode_digit(codepoint)
if codepoint - 48 < 10
codepoint - 22
elsif codepoint - 65 < 26
codepoint - 65
elsif codepoint - 97 < 26
codepoint - 97
else
PUNYCODE_BASE
end
end
private_class_method :punycode_decode_digit
# Bias adaptation method
def self.punycode_adapt(delta, numpoints, firsttime)
delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1
# delta >> 1 is a faster way of doing delta / 2
delta += delta / numpoints
difference = PUNYCODE_BASE - PUNYCODE_TMIN
k = 0
while delta > (difference * PUNYCODE_TMAX) / 2
delta /= difference
k += PUNYCODE_BASE
end
k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
end
private_class_method :punycode_adapt
end
# :startdoc:
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
# encoding:utf-8
#--
# Copyright (C) Bob Aman
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++
# Used to prevent the class/module from being loaded more than once
if !defined?(Addressable::VERSION)
module Addressable
module VERSION
MAJOR = 2
MINOR = 7
TINY = 0
STRING = [MAJOR, MINOR, TINY].join('.')
end
end
end

View File

@ -1,3 +0,0 @@
class Mechanize
VERSION = "2.7.7"
end

View File

@ -1,3 +1,4 @@
# frozen_string_literal: true
# coding: BINARY
require 'strscan'
@ -16,6 +17,7 @@ end
# * Missing disposition-type
# * Multiple semicolons
# * Whitespace around semicolons
# * Dates in ISO 8601 format
class Mechanize::HTTP::ContentDispositionParser
@ -93,7 +95,17 @@ class Mechanize::HTTP::ContentDispositionParser
when /^filename$/ then
rfc_2045_value
when /^(creation|modification|read)-date$/ then
Time.rfc822 rfc_2045_quoted_string
date = rfc_2045_quoted_string
begin
Time.rfc822 date
rescue ArgumentError
begin
Time.iso8601 date
rescue ArgumentError
nil
end
end
when /^size$/ then
rfc_2045_value.to_i(10)
else
@ -125,7 +137,7 @@ class Mechanize::HTTP::ContentDispositionParser
def rfc_2045_quoted_string
return nil unless @scanner.scan(/"/)
text = ''
text = String.new
while true do
chunk = @scanner.scan(/[\000-\014\016-\041\043-\133\135-\177]+/) # not \r "

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
class Mechanize
VERSION = "2.8.0"
end

View File

@ -0,0 +1,179 @@
# frozen_string_literal: true
# = Public Suffix
#
# Domain name parser based on the Public Suffix List.
#
# Copyright (c) 2009-2020 Simone Carletti <weppos@weppos.net>
require_relative "public_suffix/domain"
require_relative "public_suffix/version"
require_relative "public_suffix/errors"
require_relative "public_suffix/rule"
require_relative "public_suffix/list"
# PublicSuffix is a Ruby domain name parser based on the Public Suffix List.
#
# The [Public Suffix List](https://publicsuffix.org) is a cross-vendor initiative
# to provide an accurate list of domain name suffixes.
#
# The Public Suffix List is an initiative of the Mozilla Project,
# but is maintained as a community resource. It is available for use in any software,
# but was originally created to meet the needs of browser manufacturers.
module PublicSuffix
DOT = "."
BANG = "!"
STAR = "*"
# Parses +name+ and returns the {PublicSuffix::Domain} instance.
#
# @example Parse a valid domain
# PublicSuffix.parse("google.com")
# # => #<PublicSuffix::Domain:0x007fec2e51e588 @sld="google", @tld="com", @trd=nil>
#
# @example Parse a valid subdomain
# PublicSuffix.parse("www.google.com")
# # => #<PublicSuffix::Domain:0x007fec276d4cf8 @sld="google", @tld="com", @trd="www">
#
# @example Parse a fully qualified domain
# PublicSuffix.parse("google.com.")
# # => #<PublicSuffix::Domain:0x007fec257caf38 @sld="google", @tld="com", @trd=nil>
#
# @example Parse a fully qualified domain (subdomain)
# PublicSuffix.parse("www.google.com.")
# # => #<PublicSuffix::Domain:0x007fec27b6bca8 @sld="google", @tld="com", @trd="www">
#
# @example Parse an invalid (unlisted) domain
# PublicSuffix.parse("x.yz")
# # => #<PublicSuffix::Domain:0x007fec2f49bec0 @sld="x", @tld="yz", @trd=nil>
#
# @example Parse an invalid (unlisted) domain with strict checking (without applying the default * rule)
# PublicSuffix.parse("x.yz", default_rule: nil)
# # => PublicSuffix::DomainInvalid: `x.yz` is not a valid domain
#
# @example Parse an URL (not supported, only domains)
# PublicSuffix.parse("http://www.google.com")
# # => PublicSuffix::DomainInvalid: http://www.google.com is not expected to contain a scheme
#
#
# @param [String, #to_s] name The domain name or fully qualified domain name to parse.
# @param [PublicSuffix::List] list The rule list to search, defaults to the default {PublicSuffix::List}
# @param [Boolean] ignore_private
# @return [PublicSuffix::Domain]
#
# @raise [PublicSuffix::DomainInvalid]
# If domain is not a valid domain.
# @raise [PublicSuffix::DomainNotAllowed]
# If a rule for +domain+ is found, but the rule doesn't allow +domain+.
def self.parse(name, list: List.default, default_rule: list.default_rule, ignore_private: false)
what = normalize(name)
raise what if what.is_a?(DomainInvalid)
rule = list.find(what, default: default_rule, ignore_private: ignore_private)
# rubocop:disable Style/IfUnlessModifier
if rule.nil?
raise DomainInvalid, "`#{what}` is not a valid domain"
end
if rule.decompose(what).last.nil?
raise DomainNotAllowed, "`#{what}` is not allowed according to Registry policy"
end
# rubocop:enable Style/IfUnlessModifier
decompose(what, rule)
end
# Checks whether +domain+ is assigned and allowed, without actually parsing it.
#
# This method doesn't care whether domain is a domain or subdomain.
# The validation is performed using the default {PublicSuffix::List}.
#
# @example Validate a valid domain
# PublicSuffix.valid?("example.com")
# # => true
#
# @example Validate a valid subdomain
# PublicSuffix.valid?("www.example.com")
# # => true
#
# @example Validate a not-listed domain
# PublicSuffix.valid?("example.tldnotlisted")
# # => true
#
# @example Validate a not-listed domain with strict checking (without applying the default * rule)
# PublicSuffix.valid?("example.tldnotlisted")
# # => true
# PublicSuffix.valid?("example.tldnotlisted", default_rule: nil)
# # => false
#
# @example Validate a fully qualified domain
# PublicSuffix.valid?("google.com.")
# # => true
# PublicSuffix.valid?("www.google.com.")
# # => true
#
# @example Check an URL (which is not a valid domain)
# PublicSuffix.valid?("http://www.example.com")
# # => false
#
#
# @param [String, #to_s] name The domain name or fully qualified domain name to validate.
# @param [Boolean] ignore_private
# @return [Boolean]
def self.valid?(name, list: List.default, default_rule: list.default_rule, ignore_private: false)
what = normalize(name)
return false if what.is_a?(DomainInvalid)
rule = list.find(what, default: default_rule, ignore_private: ignore_private)
!rule.nil? && !rule.decompose(what).last.nil?
end
# Attempt to parse the name and returns the domain, if valid.
#
# This method doesn't raise. Instead, it returns nil if the domain is not valid for whatever reason.
#
# @param [String, #to_s] name The domain name or fully qualified domain name to parse.
# @param [PublicSuffix::List] list The rule list to search, defaults to the default {PublicSuffix::List}
# @param [Boolean] ignore_private
# @return [String]
def self.domain(name, **options)
parse(name, **options).domain
rescue PublicSuffix::Error
nil
end
# private
def self.decompose(name, rule)
left, right = rule.decompose(name)
parts = left.split(DOT)
# If we have 0 parts left, there is just a tld and no domain or subdomain
# If we have 1 part left, there is just a tld, domain and not subdomain
# If we have 2 parts left, the last part is the domain, the other parts (combined) are the subdomain
tld = right
sld = parts.empty? ? nil : parts.pop
trd = parts.empty? ? nil : parts.join(DOT)
Domain.new(tld, sld, trd)
end
# Pretend we know how to deal with user input.
def self.normalize(name)
name = name.to_s.dup
name.strip!
name.chomp!(DOT)
name.downcase!
return DomainInvalid.new("Name is blank") if name.empty?
return DomainInvalid.new("Name starts with a dot") if name.start_with?(DOT)
return DomainInvalid.new("%s is not expected to contain a scheme" % name) if name.include?("://")
name
end
end

View File

@ -0,0 +1,235 @@
# frozen_string_literal: true
# = Public Suffix
#
# Domain name parser based on the Public Suffix List.
#
# Copyright (c) 2009-2020 Simone Carletti <weppos@weppos.net>
module PublicSuffix
# Domain represents a domain name, composed by a TLD, SLD and TRD.
class Domain
# Splits a string into the labels, that is the dot-separated parts.
#
# The input is not validated, but it is assumed to be a valid domain name.
#
# @example
#
# name_to_labels('example.com')
# # => ['example', 'com']
#
# name_to_labels('example.co.uk')
# # => ['example', 'co', 'uk']
#
# @param name [String, #to_s] The domain name to split.
# @return [Array<String>]
def self.name_to_labels(name)
name.to_s.split(DOT)
end
attr_reader :tld, :sld, :trd
# Creates and returns a new {PublicSuffix::Domain} instance.
#
# @overload initialize(tld)
# Initializes with a +tld+.
# @param [String] tld The TLD (extension)
# @overload initialize(tld, sld)
# Initializes with a +tld+ and +sld+.
# @param [String] tld The TLD (extension)
# @param [String] sld The TRD (domain)
# @overload initialize(tld, sld, trd)
# Initializes with a +tld+, +sld+ and +trd+.
# @param [String] tld The TLD (extension)
# @param [String] sld The SLD (domain)
# @param [String] trd The TRD (subdomain)
#
# @yield [self] Yields on self.
# @yieldparam [PublicSuffix::Domain] self The newly creates instance
#
# @example Initialize with a TLD
# PublicSuffix::Domain.new("com")
# # => #<PublicSuffix::Domain @tld="com">
#
# @example Initialize with a TLD and SLD
# PublicSuffix::Domain.new("com", "example")
# # => #<PublicSuffix::Domain @tld="com", @trd=nil>
#
# @example Initialize with a TLD, SLD and TRD
# PublicSuffix::Domain.new("com", "example", "wwww")
# # => #<PublicSuffix::Domain @tld="com", @trd=nil, @sld="example">
#
def initialize(*args)
@tld, @sld, @trd = args
yield(self) if block_given?
end
# Returns a string representation of this object.
#
# @return [String]
def to_s
name
end
# Returns an array containing the domain parts.
#
# @return [Array<String, nil>]
#
# @example
#
# PublicSuffix::Domain.new("google.com").to_a
# # => [nil, "google", "com"]
#
# PublicSuffix::Domain.new("www.google.com").to_a
# # => [nil, "google", "com"]
#
def to_a
[@trd, @sld, @tld]
end
# Returns the full domain name.
#
# @return [String]
#
# @example Gets the domain name of a domain
# PublicSuffix::Domain.new("com", "google").name
# # => "google.com"
#
# @example Gets the domain name of a subdomain
# PublicSuffix::Domain.new("com", "google", "www").name
# # => "www.google.com"
#
def name
[@trd, @sld, @tld].compact.join(DOT)
end
# Returns a domain-like representation of this object
# if the object is a {#domain?}, <tt>nil</tt> otherwise.
#
# PublicSuffix::Domain.new("com").domain
# # => nil
#
# PublicSuffix::Domain.new("com", "google").domain
# # => "google.com"
#
# PublicSuffix::Domain.new("com", "google", "www").domain
# # => "www.google.com"
#
# This method doesn't validate the input. It handles the domain
# as a valid domain name and simply applies the necessary transformations.
#
# This method returns a FQD, not just the domain part.
# To get the domain part, use <tt>#sld</tt> (aka second level domain).
#
# PublicSuffix::Domain.new("com", "google", "www").domain
# # => "google.com"
#
# PublicSuffix::Domain.new("com", "google", "www").sld
# # => "google"
#
# @see #domain?
# @see #subdomain
#
# @return [String]
def domain
[@sld, @tld].join(DOT) if domain?
end
# Returns a subdomain-like representation of this object
# if the object is a {#subdomain?}, <tt>nil</tt> otherwise.
#
# PublicSuffix::Domain.new("com").subdomain
# # => nil
#
# PublicSuffix::Domain.new("com", "google").subdomain
# # => nil
#
# PublicSuffix::Domain.new("com", "google", "www").subdomain
# # => "www.google.com"
#
# This method doesn't validate the input. It handles the domain
# as a valid domain name and simply applies the necessary transformations.
#
# This method returns a FQD, not just the subdomain part.
# To get the subdomain part, use <tt>#trd</tt> (aka third level domain).
#
# PublicSuffix::Domain.new("com", "google", "www").subdomain
# # => "www.google.com"
#
# PublicSuffix::Domain.new("com", "google", "www").trd
# # => "www"
#
# @see #subdomain?
# @see #domain
#
# @return [String]
def subdomain
[@trd, @sld, @tld].join(DOT) if subdomain?
end
# Checks whether <tt>self</tt> looks like a domain.
#
# This method doesn't actually validate the domain.
# It only checks whether the instance contains
# a value for the {#tld} and {#sld} attributes.
#
# @example
#
# PublicSuffix::Domain.new("com").domain?
# # => false
#
# PublicSuffix::Domain.new("com", "google").domain?
# # => true
#
# PublicSuffix::Domain.new("com", "google", "www").domain?
# # => true
#
# # This is an invalid domain, but returns true
# # because this method doesn't validate the content.
# PublicSuffix::Domain.new("com", nil).domain?
# # => true
#
# @see #subdomain?
#
# @return [Boolean]
def domain?
!(@tld.nil? || @sld.nil?)
end
# Checks whether <tt>self</tt> looks like a subdomain.
#
# This method doesn't actually validate the subdomain.
# It only checks whether the instance contains
# a value for the {#tld}, {#sld} and {#trd} attributes.
# If you also want to validate the domain,
# use {#valid_subdomain?} instead.
#
# @example
#
# PublicSuffix::Domain.new("com").subdomain?
# # => false
#
# PublicSuffix::Domain.new("com", "google").subdomain?
# # => false
#
# PublicSuffix::Domain.new("com", "google", "www").subdomain?
# # => true
#
# # This is an invalid domain, but returns true
# # because this method doesn't validate the content.
# PublicSuffix::Domain.new("com", "example", nil).subdomain?
# # => true
#
# @see #domain?
#
# @return [Boolean]
def subdomain?
!(@tld.nil? || @sld.nil? || @trd.nil?)
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
# = Public Suffix
#
# Domain name parser based on the Public Suffix List.
#
# Copyright (c) 2009-2020 Simone Carletti <weppos@weppos.net>
module PublicSuffix
class Error < StandardError
end
# Raised when trying to parse an invalid name.
# A name is considered invalid when no rule is found in the definition list.
#
# @example
#
# PublicSuffix.parse("nic.test")
# # => PublicSuffix::DomainInvalid
#
# PublicSuffix.parse("http://www.nic.it")
# # => PublicSuffix::DomainInvalid
#
class DomainInvalid < Error
end
# Raised when trying to parse a name that matches a suffix.
#
# @example
#
# PublicSuffix.parse("nic.do")
# # => PublicSuffix::DomainNotAllowed
#
# PublicSuffix.parse("www.nic.do")
# # => PublicSuffix::Domain
#
class DomainNotAllowed < DomainInvalid
end
end

View File

@ -0,0 +1,247 @@
# frozen_string_literal: true
# = Public Suffix
#
# Domain name parser based on the Public Suffix List.
#
# Copyright (c) 2009-2020 Simone Carletti <weppos@weppos.net>
module PublicSuffix
# A {PublicSuffix::List} is a collection of one
# or more {PublicSuffix::Rule}.
#
# Given a {PublicSuffix::List},
# you can add or remove {PublicSuffix::Rule},
# iterate all items in the list or search for the first rule
# which matches a specific domain name.
#
# # Create a new list
# list = PublicSuffix::List.new
#
# # Push two rules to the list
# list << PublicSuffix::Rule.factory("it")
# list << PublicSuffix::Rule.factory("com")
#
# # Get the size of the list
# list.size
# # => 2
#
# # Search for the rule matching given domain
# list.find("example.com")
# # => #<PublicSuffix::Rule::Normal>
# list.find("example.org")
# # => nil
#
# You can create as many {PublicSuffix::List} you want.
# The {PublicSuffix::List.default} rule list is used
# to tokenize and validate a domain.
#
class List
DEFAULT_LIST_PATH = File.expand_path("../../data/list.txt", __dir__)
# Gets the default rule list.
#
# Initializes a new {PublicSuffix::List} parsing the content
# of {PublicSuffix::List.default_list_content}, if required.
#
# @return [PublicSuffix::List]
def self.default(**options)
@default ||= parse(File.read(DEFAULT_LIST_PATH), **options)
end
# Sets the default rule list to +value+.
#
# @param value [PublicSuffix::List] the new list
# @return [PublicSuffix::List]
def self.default=(value)
@default = value
end
# Parse given +input+ treating the content as Public Suffix List.
#
# See http://publicsuffix.org/format/ for more details about input format.
#
# @param string [#each_line] the list to parse
# @param private_domains [Boolean] whether to ignore the private domains section
# @return [PublicSuffix::List]
def self.parse(input, private_domains: true)
comment_token = "//"
private_token = "===BEGIN PRIVATE DOMAINS==="
section = nil # 1 == ICANN, 2 == PRIVATE
new do |list|
input.each_line do |line|
line.strip!
case # rubocop:disable Style/EmptyCaseCondition
# skip blank lines
when line.empty?
next
# include private domains or stop scanner
when line.include?(private_token)
break if !private_domains
section = 2
# skip comments
when line.start_with?(comment_token)
next
else
list.add(Rule.factory(line, private: section == 2))
end
end
end
end
# Initializes an empty {PublicSuffix::List}.
#
# @yield [self] Yields on self.
# @yieldparam [PublicSuffix::List] self The newly created instance.
def initialize
@rules = {}
yield(self) if block_given?
end
# Checks whether two lists are equal.
#
# List <tt>one</tt> is equal to <tt>two</tt>, if <tt>two</tt> is an instance of
# {PublicSuffix::List} and each +PublicSuffix::Rule::*+
# in list <tt>one</tt> is available in list <tt>two</tt>, in the same order.
#
# @param other [PublicSuffix::List] the List to compare
# @return [Boolean]
def ==(other)
return false unless other.is_a?(List)
equal?(other) || @rules == other.rules
end
alias eql? ==
# Iterates each rule in the list.
def each(&block)
Enumerator.new do |y|
@rules.each do |key, node|
y << entry_to_rule(node, key)
end
end.each(&block)
end
# Adds the given object to the list and optionally refreshes the rule index.
#
# @param rule [PublicSuffix::Rule::*] the rule to add to the list
# @return [self]
def add(rule)
@rules[rule.value] = rule_to_entry(rule)
self
end
alias << add
# Gets the number of rules in the list.
#
# @return [Integer]
def size
@rules.size
end
# Checks whether the list is empty.
#
# @return [Boolean]
def empty?
@rules.empty?
end
# Removes all rules.
#
# @return [self]
def clear
@rules.clear
self
end
# Finds and returns the rule corresponding to the longest public suffix for the hostname.
#
# @param name [#to_s] the hostname
# @param default [PublicSuffix::Rule::*] the default rule to return in case no rule matches
# @return [PublicSuffix::Rule::*]
def find(name, default: default_rule, **options)
rule = select(name, **options).inject do |l, r|
return r if r.class == Rule::Exception
l.length > r.length ? l : r
end
rule || default
end
# Selects all the rules matching given hostame.
#
# If `ignore_private` is set to true, the algorithm will skip the rules that are flagged as
# private domain. Note that the rules will still be part of the loop.
# If you frequently need to access lists ignoring the private domains,
# you should create a list that doesn't include these domains setting the
# `private_domains: false` option when calling {.parse}.
#
# Note that this method is currently private, as you should not rely on it. Instead,
# the public interface is {#find}. The current internal algorithm allows to return all
# matching rules, but different data structures may not be able to do it, and instead would
# return only the match. For this reason, you should rely on {#find}.
#
# @param name [#to_s] the hostname
# @param ignore_private [Boolean]
# @return [Array<PublicSuffix::Rule::*>]
def select(name, ignore_private: false)
name = name.to_s
parts = name.split(DOT).reverse!
index = 0
query = parts[index]
rules = []
loop do
match = @rules[query]
rules << entry_to_rule(match, query) if !match.nil? && (ignore_private == false || match.private == false)
index += 1
break if index >= parts.size
query = parts[index] + DOT + query
end
rules
end
private :select
# Gets the default rule.
#
# @see PublicSuffix::Rule.default_rule
#
# @return [PublicSuffix::Rule::*]
def default_rule
PublicSuffix::Rule.default
end
protected
attr_reader :rules
private
def entry_to_rule(entry, value)
entry.type.new(value: value, length: entry.length, private: entry.private)
end
def rule_to_entry(rule)
Rule::Entry.new(rule.class, rule.length, rule.private)
end
end
end

View File

@ -0,0 +1,350 @@
# frozen_string_literal: true
# = Public Suffix
#
# Domain name parser based on the Public Suffix List.
#
# Copyright (c) 2009-2020 Simone Carletti <weppos@weppos.net>
module PublicSuffix
# A Rule is a special object which holds a single definition
# of the Public Suffix List.
#
# There are 3 types of rules, each one represented by a specific
# subclass within the +PublicSuffix::Rule+ namespace.
#
# To create a new Rule, use the {PublicSuffix::Rule#factory} method.
#
# PublicSuffix::Rule.factory("ar")
# # => #<PublicSuffix::Rule::Normal>
#
module Rule
# @api internal
Entry = Struct.new(:type, :length, :private) # rubocop:disable Lint/StructNewOverride
# = Abstract rule class
#
# This represent the base class for a Rule definition
# in the {Public Suffix List}[https://publicsuffix.org].
#
# This is intended to be an Abstract class
# and you shouldn't create a direct instance. The only purpose
# of this class is to expose a common interface
# for all the available subclasses.
#
# * {PublicSuffix::Rule::Normal}
# * {PublicSuffix::Rule::Exception}
# * {PublicSuffix::Rule::Wildcard}
#
# ## Properties
#
# A rule is composed by 4 properties:
#
# value - A normalized version of the rule name.
# The normalization process depends on rule tpe.
#
# Here's an example
#
# PublicSuffix::Rule.factory("*.google.com")
# #<PublicSuffix::Rule::Wildcard:0x1015c14b0
# @value="google.com"
# >
#
# ## Rule Creation
#
# The best way to create a new rule is passing the rule name
# to the <tt>PublicSuffix::Rule.factory</tt> method.
#
# PublicSuffix::Rule.factory("com")
# # => PublicSuffix::Rule::Normal
#
# PublicSuffix::Rule.factory("*.com")
# # => PublicSuffix::Rule::Wildcard
#
# This method will detect the rule type and create an instance
# from the proper rule class.
#
# ## Rule Usage
#
# A rule describes the composition of a domain name and explains how to tokenize
# the name into tld, sld and trd.
#
# To use a rule, you first need to be sure the name you want to tokenize
# can be handled by the current rule.
# You can use the <tt>#match?</tt> method.
#
# rule = PublicSuffix::Rule.factory("com")
#
# rule.match?("google.com")
# # => true
#
# rule.match?("google.com")
# # => false
#
# Rule order is significant. A name can match more than one rule.
# See the {Public Suffix Documentation}[http://publicsuffix.org/format/]
# to learn more about rule priority.
#
# When you have the right rule, you can use it to tokenize the domain name.
#
# rule = PublicSuffix::Rule.factory("com")
#
# rule.decompose("google.com")
# # => ["google", "com"]
#
# rule.decompose("www.google.com")
# # => ["www.google", "com"]
#
# @abstract
#
class Base
# @return [String] the rule definition
attr_reader :value
# @return [String] the length of the rule
attr_reader :length
# @return [Boolean] true if the rule is a private domain
attr_reader :private
# Initializes a new rule from the content.
#
# @param content [String] the content of the rule
# @param private [Boolean]
def self.build(content, private: false)
new(value: content, private: private)
end
# Initializes a new rule.
#
# @param value [String]
# @param private [Boolean]
def initialize(value:, length: nil, private: false)
@value = value.to_s
@length = length || @value.count(DOT) + 1
@private = private
end
# Checks whether this rule is equal to <tt>other</tt>.
#
# @param [PublicSuffix::Rule::*] other The rule to compare
# @return [Boolean]
# Returns true if this rule and other are instances of the same class
# and has the same value, false otherwise.
def ==(other)
equal?(other) || (self.class == other.class && value == other.value)
end
alias eql? ==
# Checks if this rule matches +name+.
#
# A domain name is said to match a rule if and only if
# all of the following conditions are met:
#
# - When the domain and rule are split into corresponding labels,
# that the domain contains as many or more labels than the rule.
# - Beginning with the right-most labels of both the domain and the rule,
# and continuing for all labels in the rule, one finds that for every pair,
# either they are identical, or that the label from the rule is "*".
#
# @see https://publicsuffix.org/list/
#
# @example
# PublicSuffix::Rule.factory("com").match?("example.com")
# # => true
# PublicSuffix::Rule.factory("com").match?("example.net")
# # => false
#
# @param name [String] the domain name to check
# @return [Boolean]
def match?(name)
# Note: it works because of the assumption there are no
# rules like foo.*.com. If the assumption is incorrect,
# we need to properly walk the input and skip parts according
# to wildcard component.
diff = name.chomp(value)
diff.empty? || diff.end_with?(DOT)
end
# @abstract
def parts
raise NotImplementedError
end
# @abstract
# @param [String, #to_s] name The domain name to decompose
# @return [Array<String, nil>]
def decompose(*)
raise NotImplementedError
end
end
# Normal represents a standard rule (e.g. com).
class Normal < Base
# Gets the original rule definition.
#
# @return [String] The rule definition.
def rule
value
end
# Decomposes the domain name according to rule properties.
#
# @param [String, #to_s] name The domain name to decompose
# @return [Array<String>] The array with [trd + sld, tld].
def decompose(domain)
suffix = parts.join('\.')
matches = domain.to_s.match(/^(.*)\.(#{suffix})$/)
matches ? matches[1..2] : [nil, nil]
end
# dot-split rule value and returns all rule parts
# in the order they appear in the value.
#
# @return [Array<String>]
def parts
@value.split(DOT)
end
end
# Wildcard represents a wildcard rule (e.g. *.co.uk).
class Wildcard < Base
# Initializes a new rule from the content.
#
# @param content [String] the content of the rule
# @param private [Boolean]
def self.build(content, private: false)
new(value: content.to_s[2..-1], private: private)
end
# Initializes a new rule.
#
# @param value [String]
# @param private [Boolean]
def initialize(value:, length: nil, private: false)
super(value: value, length: length, private: private)
length or @length += 1 # * counts as 1
end
# Gets the original rule definition.
#
# @return [String] The rule definition.
def rule
value == "" ? STAR : STAR + DOT + value
end
# Decomposes the domain name according to rule properties.
#
# @param [String, #to_s] name The domain name to decompose
# @return [Array<String>] The array with [trd + sld, tld].
def decompose(domain)
suffix = ([".*?"] + parts).join('\.')
matches = domain.to_s.match(/^(.*)\.(#{suffix})$/)
matches ? matches[1..2] : [nil, nil]
end
# dot-split rule value and returns all rule parts
# in the order they appear in the value.
#
# @return [Array<String>]
def parts
@value.split(DOT)
end
end
# Exception represents an exception rule (e.g. !parliament.uk).
class Exception < Base
# Initializes a new rule from the content.
#
# @param content [String] the content of the rule
# @param private [Boolean]
def self.build(content, private: false)
new(value: content.to_s[1..-1], private: private)
end
# Gets the original rule definition.
#
# @return [String] The rule definition.
def rule
BANG + value
end
# Decomposes the domain name according to rule properties.
#
# @param [String, #to_s] name The domain name to decompose
# @return [Array<String>] The array with [trd + sld, tld].
def decompose(domain)
suffix = parts.join('\.')
matches = domain.to_s.match(/^(.*)\.(#{suffix})$/)
matches ? matches[1..2] : [nil, nil]
end
# dot-split rule value and returns all rule parts
# in the order they appear in the value.
# The leftmost label is not considered a label.
#
# See http://publicsuffix.org/format/:
# If the prevailing rule is a exception rule,
# modify it by removing the leftmost label.
#
# @return [Array<String>]
def parts
@value.split(DOT)[1..-1]
end
end
# Takes the +name+ of the rule, detects the specific rule class
# and creates a new instance of that class.
# The +name+ becomes the rule +value+.
#
# @example Creates a Normal rule
# PublicSuffix::Rule.factory("ar")
# # => #<PublicSuffix::Rule::Normal>
#
# @example Creates a Wildcard rule
# PublicSuffix::Rule.factory("*.ar")
# # => #<PublicSuffix::Rule::Wildcard>
#
# @example Creates an Exception rule
# PublicSuffix::Rule.factory("!congresodelalengua3.ar")
# # => #<PublicSuffix::Rule::Exception>
#
# @param [String] content The rule content.
# @return [PublicSuffix::Rule::*] A rule instance.
def self.factory(content, private: false)
case content.to_s[0, 1]
when STAR
Wildcard
when BANG
Exception
else
Normal
end.build(content, private: private)
end
# The default rule to use if no rule match.
#
# The default rule is "*". From https://publicsuffix.org/list/:
#
# > If no rules match, the prevailing rule is "*".
#
# @return [PublicSuffix::Rule::Wildcard] The default rule.
def self.default
factory(STAR)
end
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
#
# = Public Suffix
#
# Domain name parser based on the Public Suffix List.
#
# Copyright (c) 2009-2020 Simone Carletti <weppos@weppos.net>
module PublicSuffix
# The current library version.
VERSION = "4.0.6"
end

View File

@ -0,0 +1,266 @@
# encoding: UTF-8
#
# = net/ntlm.rb
#
# An NTLM Authentication Library for Ruby
#
# This code is a derivative of "dbf2.rb" written by yrock
# and Minero Aoki. You can find original code here:
# http://jp.rubyist.net/magazine/?0013-CodeReview
# -------------------------------------------------------------
# Copyright (c) 2005,2006 yrock
#
#
# 2006-02-11 refactored by Minero Aoki
# -------------------------------------------------------------
#
# All protocol information used to write this code stems from
# "The NTLM Authentication Protocol" by Eric Glass. The author
# would thank to him for this tremendous work and making it
# available on the net.
# http://davenport.sourceforge.net/ntlm.html
# -------------------------------------------------------------
# Copyright (c) 2003 Eric Glass
#
# -------------------------------------------------------------
#
# The author also looked Mozilla-Firefox-1.0.7 source code,
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
# Jonathan Bastien-Filiatrault's libntlm-ruby.
# "http://x2a.org/websvn/filedetails.php?
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
# The latter has a minor bug in its separate_keys function.
# The third key has to begin from the 14th character of the
# input string instead of 13th:)
#--
# $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
#++
require 'base64'
require 'openssl'
require 'openssl/digest'
require 'socket'
# Load Order is important here
require 'net/ntlm/exceptions'
require 'net/ntlm/field'
require 'net/ntlm/int16_le'
require 'net/ntlm/int32_le'
require 'net/ntlm/int64_le'
require 'net/ntlm/string'
require 'net/ntlm/field_set'
require 'net/ntlm/blob'
require 'net/ntlm/security_buffer'
require 'net/ntlm/message'
require 'net/ntlm/message/type0'
require 'net/ntlm/message/type1'
require 'net/ntlm/message/type2'
require 'net/ntlm/message/type3'
require 'net/ntlm/encode_util'
require 'net/ntlm/client'
require 'net/ntlm/channel_binding'
require 'net/ntlm/target_info'
module Net
module NTLM
LM_MAGIC = "KGS!@\#$%"
TIME_OFFSET = 11644473600
MAX64 = 0xffffffffffffffff
class << self
# Valid format for LAN Manager hex digest portion: 32 hexadecimal characters.
LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
# Valid format for NT LAN Manager hex digest portion: 32 hexadecimal characters.
NT_LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
# Valid format for an NTLM hash composed of `'<LAN Manager hex digest>:<NT LAN Manager hex digest>'`.
DATA_REGEXP = /\A#{LAN_MANAGER_HEX_DIGEST_REGEXP}:#{NT_LAN_MANAGER_HEX_DIGEST_REGEXP}\z/
# Takes a string and determines whether it is a valid NTLM Hash
# @param [String] the string to validate
# @return [Boolean] whether or not the string is a valid NTLM hash
def is_ntlm_hash?(data)
decoded_data = data.dup
decoded_data = EncodeUtil.decode_utf16le(decoded_data)
if DATA_REGEXP.match(decoded_data)
true
else
false
end
end
# Conver the value to a 64-Bit Little Endian Int
# @param [String] val The string to convert
def pack_int64le(val)
[val & 0x00000000ffffffff, val >> 32].pack("V2")
end
# Builds an array of strings that are 7 characters long
# @param [String] str The string to split
# @api private
def split7(str)
s = str.dup
until s.empty?
(ret ||= []).push s.slice!(0, 7)
end
ret
end
# Not sure what this is doing
# @param [String] str String to generate keys for
# @api private
def gen_keys(str)
split7(str).map{ |str7|
bits = split7(str7.unpack("B*")[0]).inject('')\
{|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
[bits].pack("B*")
}
end
def apply_des(plain, keys)
dec = OpenSSL::Cipher.new("des-cbc").encrypt
dec.padding = 0
keys.map {|k|
dec.key = k
dec.update(plain) + dec.final
}
end
# Generates a Lan Manager Hash
# @param [String] password The password to base the hash on
def lm_hash(password)
keys = gen_keys password.upcase.ljust(14, "\0")
apply_des(LM_MAGIC, keys).join
end
# Generate a NTLM Hash
# @param [String] password The password to base the hash on
# @option opt :unicode (false) Unicode encode the password
def ntlm_hash(password, opt = {})
pwd = password.dup
unless opt[:unicode]
pwd = EncodeUtil.encode_utf16le(pwd)
end
OpenSSL::Digest::MD4.digest pwd
end
# Generate a NTLMv2 Hash
# @param [String] user The username
# @param [String] password The password
# @param [String] target The domain or workstation to authenticate to
# @option opt :unicode (false) Unicode encode the domain
def ntlmv2_hash(user, password, target, opt={})
if is_ntlm_hash? password
decoded_password = EncodeUtil.decode_utf16le(password)
ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
else
ntlmhash = ntlm_hash(password, opt)
end
userdomain = user.upcase + target
unless opt[:unicode]
userdomain = EncodeUtil.encode_utf16le(userdomain)
end
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
end
def lm_response(arg)
begin
hash = arg[:lm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
keys = gen_keys hash.ljust(21, "\0")
apply_des(chal, keys).join
end
def ntlm_response(arg)
hash = arg[:ntlm_hash]
chal = arg[:challenge]
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
keys = gen_keys hash.ljust(21, "\0")
apply_des(chal, keys).join
end
def ntlmv2_response(arg, opt = {})
begin
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
ti = arg[:target_info]
rescue
raise ArgumentError
end
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
if opt[:timestamp]
ts = opt[:timestamp]
else
ts = Time.now.to_i
end
# epoch -> milsec from Jan 1, 1601
ts = 10_000_000 * (ts + TIME_OFFSET)
blob = Blob.new
blob.timestamp = ts
blob.challenge = cc
blob.target_info = ti
bb = blob.serialize
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
end
def lmv2_response(arg, opt = {})
key = arg[:ntlmv2_hash]
chal = arg[:challenge]
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
end
def ntlm2_session(arg, opt = {})
begin
passwd_hash = arg[:ntlm_hash]
chal = arg[:challenge]
rescue
raise ArgumentError
end
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
keys = gen_keys(passwd_hash.ljust(21, "\0"))
session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
response = apply_des(session_hash, keys).join
[cc.ljust(24, "\0"), response]
end
end
end
end

View File

@ -0,0 +1,28 @@
module Net
module NTLM
BLOB_SIGN = 0x00000101
class Blob < FieldSet
int32LE :blob_signature, {:value => BLOB_SIGN}
int32LE :reserved, {:value => 0}
int64LE :timestamp, {:value => 0}
string :challenge, {:value => "", :size => 8}
int32LE :unknown1, {:value => 0}
string :target_info, {:value => "", :size => 0}
int32LE :unknown2, {:value => 0}
def parse(str, offset=0)
# 28 is the length of all fields before the variable-length
# target_info field.
if str.size > 28
enable(:target_info)
# Grab everything except the last 4 bytes (which will be :unknown2)
self[:target_info].value = str[28..-5]
end
super
end
end
end
end

View File

@ -0,0 +1,65 @@
module Net
module NTLM
class ChannelBinding
# Creates a ChannelBinding used for Extended Protection Authentication
# @see http://blogs.msdn.com/b/openspecification/archive/2013/03/26/ntlm-and-channel-binding-hash-aka-exteneded-protection-for-authentication.aspx
#
# @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing
# the outer TLS channel
# @return [NTLM::ChannelBinding] A ChannelBinding holding a token that can be
# embedded in a {Type3} message
def self.create(outer_channel)
new(outer_channel)
end
# @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing
# the outer TLS channel
def initialize(outer_channel)
@channel = outer_channel
@unique_prefix = 'tls-server-end-point'
@initiator_addtype = 0
@initiator_address_length = 0
@acceptor_addrtype = 0
@acceptor_address_length = 0
end
attr_reader :channel, :unique_prefix, :initiator_addtype
attr_reader :initiator_address_length, :acceptor_addrtype
attr_reader :acceptor_address_length
# Returns a channel binding hash acceptable for use as a AV_PAIR MsvAvChannelBindings
# field value as specified in the NTLM protocol
#
# @return [String] MD5 hash of gss_channel_bindings_struct
def channel_binding_token
@channel_binding_token ||= OpenSSL::Digest::MD5.new(gss_channel_bindings_struct).digest
end
def gss_channel_bindings_struct
@gss_channel_bindings_struct ||= begin
token = [initiator_addtype].pack('I')
token << [initiator_address_length].pack('I')
token << [acceptor_addrtype].pack('I')
token << [acceptor_address_length].pack('I')
token << [application_data.length].pack('I')
token << application_data
token
end
end
def channel_hash
@channel_hash ||= OpenSSL::Digest::SHA256.new(channel.to_der)
end
def application_data
@application_data ||= begin
data = unique_prefix
data << ':'
data << channel_hash.digest
data
end
end
end
end
end

View File

@ -0,0 +1,65 @@
module Net
module NTLM
class Client
DEFAULT_FLAGS = NTLM::FLAGS[:UNICODE] | NTLM::FLAGS[:OEM] |
NTLM::FLAGS[:SIGN] | NTLM::FLAGS[:SEAL] | NTLM::FLAGS[:REQUEST_TARGET] |
NTLM::FLAGS[:NTLM] | NTLM::FLAGS[:ALWAYS_SIGN] | NTLM::FLAGS[:NTLM2_KEY] |
NTLM::FLAGS[:KEY128] | NTLM::FLAGS[:KEY_EXCHANGE] | NTLM::FLAGS[:KEY56]
attr_reader :username, :password, :domain, :workstation, :flags
# @note All string parameters should be encoded in UTF-8. The proper
# final encoding for placing in the various {Message messages} will be
# chosen based on negotiation with the server.
#
# @param username [String]
# @param password [String]
# @option opts [String] :domain where we're authenticating to
# @option opts [String] :workstation local workstation name
# @option opts [Fixnum] :flags (DEFAULT_FLAGS) see Net::NTLM::Message::Type1.flag
def initialize(username, password, opts = {})
@username = username
@password = password
@domain = opts[:domain] || nil
@workstation = opts[:workstation] || nil
@flags = opts[:flags] || DEFAULT_FLAGS
end
# @return [NTLM::Message]
def init_context(resp = nil, channel_binding = nil)
if resp.nil?
@session = nil
type1_message
else
@session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
@session.authenticate!
end
end
# @return [Client::Session]
def session
@session
end
def session_key
@session.exported_session_key
end
private
# @return [Message::Type1]
def type1_message
type1 = Message::Type1.new
type1[:flag].value = flags
type1.domain = domain if domain
type1.workstation = workstation if workstation
type1
end
end
end
end
require "net/ntlm/client/session"

View File

@ -0,0 +1,237 @@
module Net
module NTLM
class Client::Session
VERSION_MAGIC = "\x01\x00\x00\x00"
TIME_OFFSET = 11644473600
MAX64 = 0xffffffffffffffff
CLIENT_TO_SERVER_SIGNING = "session key to client-to-server signing key magic constant\0"
SERVER_TO_CLIENT_SIGNING = "session key to server-to-client signing key magic constant\0"
CLIENT_TO_SERVER_SEALING = "session key to client-to-server sealing key magic constant\0"
SERVER_TO_CLIENT_SEALING = "session key to server-to-client sealing key magic constant\0"
attr_reader :client, :challenge_message, :channel_binding
# @param client [Net::NTLM::Client] the client instance
# @param challenge_message [Net::NTLM::Message::Type2] server message
def initialize(client, challenge_message, channel_binding = nil)
@client = client
@challenge_message = challenge_message
@channel_binding = channel_binding
end
# Generate an NTLMv2 AUTHENTICATE_MESSAGE
# @see http://msdn.microsoft.com/en-us/library/cc236643.aspx
# @return [Net::NTLM::Message::Type3]
def authenticate!
calculate_user_session_key!
type3_opts = {
:lm_response => lmv2_resp,
:ntlm_response => ntlmv2_resp,
:domain => domain,
:user => username,
:workstation => workstation,
:flag => (challenge_message.flag & client.flags)
}
t3 = Message::Type3.create type3_opts
if negotiate_key_exchange?
t3.enable(:session_key)
rc4 = OpenSSL::Cipher.new("rc4")
rc4.encrypt
rc4.key = user_session_key
sk = rc4.update exported_session_key
sk << rc4.final
t3.session_key = sk
end
t3
end
def exported_session_key
@exported_session_key ||=
begin
if negotiate_key_exchange?
OpenSSL::Cipher.new("rc4").random_key
else
user_session_key
end
end
end
def sign_message(message)
seq = sequence
sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, client_sign_key, "#{seq}#{message}")[0..7]
if negotiate_key_exchange?
sig = client_cipher.update sig
sig << client_cipher.final
end
"#{VERSION_MAGIC}#{sig}#{seq}"
end
def verify_signature(signature, message)
seq = signature[-4..-1]
sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, server_sign_key, "#{seq}#{message}")[0..7]
if negotiate_key_exchange?
sig = server_cipher.update sig
sig << server_cipher.final
end
"#{VERSION_MAGIC}#{sig}#{seq}" == signature
end
def seal_message(message)
emessage = client_cipher.update(message)
emessage + client_cipher.final
end
def unseal_message(emessage)
message = server_cipher.update(emessage)
message + server_cipher.final
end
private
def user_session_key
@user_session_key ||= nil
end
def sequence
[raw_sequence].pack("V*")
end
def raw_sequence
if defined? @raw_sequence
@raw_sequence += 1
else
@raw_sequence = 0
end
end
def client_sign_key
@client_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SIGNING}"
end
def server_sign_key
@server_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SIGNING}"
end
def client_seal_key
@client_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SEALING}"
end
def server_seal_key
@server_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SEALING}"
end
def client_cipher
@client_cipher ||=
begin
rc4 = OpenSSL::Cipher.new("rc4")
rc4.encrypt
rc4.key = client_seal_key
rc4
end
end
def server_cipher
@server_cipher ||=
begin
rc4 = OpenSSL::Cipher.new("rc4")
rc4.decrypt
rc4.key = server_seal_key
rc4
end
end
def client_challenge
@client_challenge ||= NTLM.pack_int64le(rand(MAX64))
end
def server_challenge
@server_challenge ||= challenge_message[:challenge].serialize
end
# epoch -> milsec from Jan 1, 1601
# @see http://support.microsoft.com/kb/188768
def timestamp
@timestamp ||= 10_000_000 * (Time.now.to_i + TIME_OFFSET)
end
def use_oem_strings?
challenge_message.has_flag? :OEM
end
def negotiate_key_exchange?
challenge_message.has_flag? :KEY_EXCHANGE
end
def username
oem_or_unicode_str client.username
end
def password
oem_or_unicode_str client.password
end
def workstation
(client.workstation ? oem_or_unicode_str(client.workstation) : "")
end
def domain
(client.domain ? oem_or_unicode_str(client.domain) : "")
end
def oem_or_unicode_str(str)
if use_oem_strings?
NTLM::EncodeUtil.decode_utf16le str
else
NTLM::EncodeUtil.encode_utf16le str
end
end
def ntlmv2_hash
@ntlmv2_hash ||= NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?})
end
def calculate_user_session_key!
@user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
end
def lmv2_resp
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + client_challenge) + client_challenge
end
def ntlmv2_resp
nt_proof_str + blob
end
def nt_proof_str
@nt_proof_str ||= OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + blob)
end
def blob
@blob ||=
begin
b = Blob.new
b.timestamp = timestamp
b.challenge = client_challenge
b.target_info = target_info
b.serialize
end
end
def target_info
@target_info ||= begin
if channel_binding
t = Net::NTLM::TargetInfo.new(challenge_message.target_info)
av_id = Net::NTLM::TargetInfo::MSV_AV_CHANNEL_BINDINGS
t.av_pairs[av_id] = channel_binding.channel_binding_token
t.to_s
else
challenge_message.target_info
end
end
end
end
end
end

View File

@ -0,0 +1,48 @@
module Net
module NTLM
class EncodeUtil
if RUBY_VERSION == "1.8.7"
require "kconv"
# Decode a UTF16 string to a ASCII string
# @param [String] str The string to convert
def self.decode_utf16le(str)
Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
end
# Encodes a ASCII string to a UTF16 string
# @param [String] str The string to convert
def self.encode_utf16le(str)
swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
end
# Taggle the strings endianness between big/little and little/big
# @param [String] str The string to swap the endianness on
def self.swap16(str)
str.unpack("v*").pack("n*")
end
else # Use native 1.9 string encoding functions
# Decode a UTF16 string to a ASCII string
# @param [String] str The string to convert
def self.decode_utf16le(str)
str = str.dup.force_encoding(Encoding::UTF_16LE)
str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
end
# Encodes a ASCII string to a UTF16 string
# @param [String] str The string to convert
# @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable
# encodings. This library uses string contatination to build the packet bytes. The end result is that
# you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le
# the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte
# concatination works seamlessly.
def self.encode_utf16le(str)
str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('UTF-8')
end
end
end
end
end

View File

@ -0,0 +1,14 @@
module Net
module NTLM
class NtlmError < StandardError; end
class InvalidTargetDataError < NtlmError
attr_reader :data
def initialize(msg, data)
@data = data
super(msg)
end
end
end
end

View File

@ -0,0 +1,35 @@
module Net
module NTLM
# base classes for primitives
# @private
class Field
attr_accessor :active, :value
def initialize(opts)
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
@size = opts[:size].nil? ? 0 : opts[:size]
end
def size
@active ? @size : 0
end
# Serializer function for field data
# Exists in this class to be overridden by child classes
def serialize
raise NotImplementedError
end
# Parser function for field data
# Exists in this class to be overridden by child classes
def parse(str, offset=0)
raise NotImplementedError
end
end
end
end

View File

@ -0,0 +1,129 @@
module Net
module NTLM
# base class of data structure
class FieldSet
class << FieldSet
# @macro string_security_buffer
# @method $1
# @method $1=
# @return [String]
def string(name, opts)
add_field(name, Net::NTLM::String, opts)
end
# @macro int16le_security_buffer
# @method $1
# @method $1=
# @return [Int16LE]
def int16LE(name, opts)
add_field(name, Net::NTLM::Int16LE, opts)
end
# @macro int32le_security_buffer
# @method $1
# @method $1=
# @return [Int32LE]
def int32LE(name, opts)
add_field(name, Net::NTLM::Int32LE, opts)
end
# @macro int64le_security_buffer
# @method $1
# @method $1=
# @return [Int64]
def int64LE(name, opts)
add_field(name, Net::NTLM::Int64LE, opts)
end
# @macro security_buffer
# @method $1
# @method $1=
# @return [SecurityBuffer]
def security_buffer(name, opts)
add_field(name, Net::NTLM::SecurityBuffer, opts)
end
def prototypes
@proto
end
def names
return [] if @proto.nil?
@proto.map{|n, t, o| n}
end
def types
return [] if @proto.nil?
@proto.map{|n, t, o| t}
end
def opts
return [] if @proto.nil?
@proto.map{|n, t, o| o}
end
private
def add_field(name, type, opts)
(@proto ||= []).push [name, type, opts]
define_accessor name
end
def define_accessor(name)
module_eval(<<-End, __FILE__, __LINE__ + 1)
def #{name}
self['#{name}'].value
end
def #{name}=(val)
self['#{name}'].value = val
end
End
end
end
def initialize
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
end
def parse(str, offset=0)
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
end
def serialize
@alist.map{|n, f| f.serialize }.join
end
def size
@alist.inject(0){|sum, a| sum += a[1].size}
end
def [](name)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1]
end
def []=(name, val)
a = @alist.assoc(name.to_s.intern)
raise ArgumentError, "no such field: #{name}" unless a
a[1] = val
end
def enable(name)
self[name].active = true
end
def disable(name)
self[name].active = false
end
def has_disabled_fields?
@alist.any? { |name, field| !field.active }
end
end
end
end

View File

@ -0,0 +1,26 @@
module Net
module NTLM
class Int16LE < Field
def initialize(opt)
super(opt)
@size = 2
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size].unpack("v")[0]
@size
else
0
end
end
def serialize
[@value].pack("v")
end
end
end
end

View File

@ -0,0 +1,25 @@
module Net
module NTLM
class Int32LE < Field
def initialize(opt)
super(opt)
@size = 4
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str.slice(offset, @size).unpack("V")[0]
@size
else
0
end
end
def serialize
[@value].pack("V") if @active
end
end
end
end

View File

@ -0,0 +1,26 @@
module Net
module NTLM
class Int64LE < Field
def initialize(opt)
super(opt)
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
d, u = str.slice(offset, @size).unpack("V2")
@value = (u * 0x100000000 + d)
@size
else
0
end
end
def serialize
[@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
end
end
end
end

View File

@ -0,0 +1,129 @@
module Net
module NTLM
SSP_SIGN = "NTLMSSP\0"
FLAGS = {
:UNICODE => 0x00000001,
:OEM => 0x00000002,
:REQUEST_TARGET => 0x00000004,
:MBZ9 => 0x00000008,
:SIGN => 0x00000010,
:SEAL => 0x00000020,
:NEG_DATAGRAM => 0x00000040,
:NETWARE => 0x00000100,
:NTLM => 0x00000200,
:NEG_NT_ONLY => 0x00000400,
:MBZ7 => 0x00000800,
:DOMAIN_SUPPLIED => 0x00001000,
:WORKSTATION_SUPPLIED => 0x00002000,
:LOCAL_CALL => 0x00004000,
:ALWAYS_SIGN => 0x00008000,
:TARGET_TYPE_DOMAIN => 0x00010000,
:NTLM2_KEY => 0x00080000,
:TARGET_INFO => 0x00800000,
:KEY128 => 0x20000000,
:KEY_EXCHANGE => 0x40000000,
:KEY56 => 0x80000000
}.freeze
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
DEFAULT_FLAGS = {
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
:TYPE2 => FLAGS[:UNICODE],
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
}
# @private false
class Message < FieldSet
class << Message
def parse(str)
m = Type0.new
m.parse(str)
case m.type
when 1
t = Type1.new.parse(str)
when 2
t = Type2.new.parse(str)
when 3
t = Type3.new.parse(str)
else
raise ArgumentError, "unknown type: #{m.type}"
end
t
end
def decode64(str)
parse(Base64.decode64(str))
end
end
# @return [self]
def parse(str)
super
while has_disabled_fields? && serialize.size < str.size
# enable the next disabled field
self.class.names.find { |name| !self[name].active && enable(name) }
super
end
self
end
def has_flag?(flag)
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
end
def set_flag(flag)
self[:flag].value |= FLAGS[flag]
end
def dump_flags
FLAG_KEYS.each{ |k| print(k, "=", has_flag?(k), "\n") }
end
def serialize
deflag
super + security_buffers.map{|n, f| f.value}.join
end
def encode64
Base64.encode64(serialize).gsub(/\n/, '')
end
def decode64(str)
parse(Base64.decode64(str))
end
alias head_size size
def data_size
security_buffers.inject(0){|sum, a| sum += a[1].data_size}
end
def size
head_size + data_size
end
def security_buffers
@alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
end
def deflag
security_buffers.inject(head_size){|cur, a|
a[1].offset = cur
cur += a[1].data_size
}
end
def data_edge
security_buffers.map{ |n, f| f.active ? f.offset : size}.min
end
end
end
end

View File

@ -0,0 +1,16 @@
module Net
module NTLM
class Message
# sub class definitions
class Type0 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 0}
end
end
end
end

View File

@ -0,0 +1,18 @@
module Net
module NTLM
class Message
# @private false
class Type1 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 1}
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
security_buffer :domain, {:value => ""}
security_buffer :workstation, {:value => Socket.gethostname }
string :os_version, {:size => 8, :value => "", :active => false }
end
end
end
end

View File

@ -0,0 +1,102 @@
module Net
module NTLM
class Message
# @private false
class Type2 < Message
string :sign, { :size => 8, :value => SSP_SIGN }
int32LE :type, { :value => 2 }
security_buffer :target_name, { :size => 0, :value => "" }
int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
int64LE :challenge, { :value => 0}
int64LE :context, { :value => 0, :active => false }
security_buffer :target_info, { :value => "", :active => false }
string :os_version, { :size => 8, :value => "", :active => false }
# Generates a Type 3 response based on the Type 2 Information
# @return [Type3]
# @option arg [String] :username The username to authenticate with
# @option arg [String] :password The user's password
# @option arg [String] :domain ('') The domain to authenticate to
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
# @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
# @note An empty :domain option authenticates to the local machine.
# @note The :use_default_target has precedence over the :domain option
def response(arg, opt = {})
usr = arg[:user]
pwd = arg[:password]
domain = arg[:domain] ? arg[:domain].upcase : ""
if usr.nil? or pwd.nil?
raise ArgumentError, "user and password have to be supplied"
end
if opt[:workstation]
ws = opt[:workstation]
else
ws = Socket.gethostname
end
if opt[:client_challenge]
cc = opt[:client_challenge]
else
cc = rand(MAX64)
end
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
opt[:client_challenge] = cc
if has_flag?(:OEM) and opt[:unicode]
usr = NTLM::EncodeUtil.decode_utf16le(usr)
pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
ws = NTLM::EncodeUtil.decode_utf16le(ws)
domain = NTLM::EncodeUtil.decode_utf16le(domain)
opt[:unicode] = false
end
if has_flag?(:UNICODE) and !opt[:unicode]
usr = NTLM::EncodeUtil.encode_utf16le(usr)
pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
ws = NTLM::EncodeUtil.encode_utf16le(ws)
domain = NTLM::EncodeUtil.encode_utf16le(domain)
opt[:unicode] = true
end
if opt[:use_default_target]
domain = self.target_name
end
ti = self.target_info
chal = self[:challenge].serialize
if opt[:ntlmv2]
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
lm_res = NTLM::lmv2_response(ar, opt)
ntlm_res = NTLM::ntlmv2_response(ar, opt)
elsif has_flag?(:NTLM2_KEY)
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
else
ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
lm_res = NTLM::lm_response(ar)
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
ntlm_res = NTLM::ntlm_response(ar)
end
Type3.create({
:lm_response => lm_res,
:ntlm_response => ntlm_res,
:domain => domain,
:user => usr,
:workstation => ws,
:flag => self.flag
})
end
end
end
end
end

View File

@ -0,0 +1,131 @@
module Net
module NTLM
class Message
# @private false
class Type3 < Message
string :sign, {:size => 8, :value => SSP_SIGN}
int32LE :type, {:value => 3}
security_buffer :lm_response, {:value => ""}
security_buffer :ntlm_response, {:value => ""}
security_buffer :domain, {:value => ""}
security_buffer :user, {:value => ""}
security_buffer :workstation, {:value => ""}
security_buffer :session_key, {:value => "", :active => false }
int32LE :flag, {:value => 0, :active => false }
string :os_version, {:size => 8, :active => false }
class << Type3
# Builds a Type 3 packet
# @note All options must be properly encoded with either unicode or oem encoding
# @return [Type3]
# @option arg [String] :lm_response The LM hash
# @option arg [String] :ntlm_response The NTLM hash
# @option arg [String] :domain The domain to authenticate to
# @option arg [String] :workstation The name of the calling workstation
# @option arg [String] :session_key The session key
# @option arg [Integer] :flag Flags for the packet
def create(arg, opt ={})
t = new
t.lm_response = arg[:lm_response]
t.ntlm_response = arg[:ntlm_response]
t.domain = arg[:domain]
t.user = arg[:user]
if arg[:workstation]
t.workstation = arg[:workstation]
end
if arg[:session_key]
t.enable(:session_key)
t.session_key = arg[:session_key]
end
if arg[:flag]
t.enable(:session_key)
t.enable(:flag)
t.flag = arg[:flag]
end
t
end
end
# @param server_challenge (see #password?)
def blank_password?(server_challenge)
password?('', server_challenge)
end
# @param password [String]
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
# {Type2} message for which this object is a response.
# @return [true] if +password+ was the password used to generate this
# {Type3} message
# @return [false] otherwise
def password?(password, server_challenge)
case ntlm_version
when :ntlm2_session
ntlm2_session_password?(password, server_challenge)
when :ntlmv2
ntlmv2_password?(password, server_challenge)
else
raise
end
end
# @return [Symbol]
def ntlm_version
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
:ntlm2_session
elsif ntlm_response.size == 24
:ntlmv1
elsif ntlm_response.size > 24
:ntlmv2
end
end
private
def ntlm2_session_password?(password, server_challenge)
hash = ntlm_response
_lm, empty_hash = NTLM.ntlm2_session(
{
:ntlm_hash => NTLM.ntlm_hash(password),
:challenge => server_challenge,
},
{
:client_challenge => lm_response[0,8]
}
)
hash == empty_hash
end
def ntlmv2_password?(password, server_challenge)
# The first 16 bytes of the ntlm_response are the HMAC of the blob
# that follows it.
blob = Blob.new
blob.parse(ntlm_response[16..-1])
empty_hash = NTLM.ntlmv2_response(
{
# user and domain came from the serialized data here, so
# they're already unicode
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
:challenge => server_challenge,
:target_info => blob.target_info
},
{
:client_challenge => blob.challenge,
# The blob's timestamp is already in milliseconds since 1601,
# so convert it back to epoch time first
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
}
)
empty_hash == ntlm_response
end
end
end
end
end

View File

@ -0,0 +1,48 @@
module Net
module NTLM
class SecurityBuffer < FieldSet
int16LE :length, {:value => 0}
int16LE :allocated, {:value => 0}
int32LE :offset, {:value => 0}
attr_accessor :active
def initialize(opts={})
super()
@value = opts[:value]
@active = opts[:active].nil? ? true : opts[:active]
@size = 8
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
super(str, offset)
@value = str[self.offset, self.length]
@size
else
0
end
end
def serialize
super if @active
end
def value
@value
end
def value=(val)
@value = val
self.length = self.allocated = val.size
end
def data_size
@active ? @value.size : 0
end
end
end
end

View File

@ -0,0 +1,35 @@
module Net
module NTLM
class String < Field
def initialize(opts)
super(opts)
@size = opts[:size]
end
def parse(str, offset=0)
if @active and str.size >= offset + @size
@value = str[offset, @size]
@size
else
0
end
end
def serialize
if @active
@value.to_s
else
""
end
end
def value=(val)
@value = val
@size = @value.nil? ? 0 : @value.size
@active = (@size > 0)
end
end
end
end

View File

@ -0,0 +1,89 @@
module Net
module NTLM
# Represents a list of AV_PAIR structures
# @see https://msdn.microsoft.com/en-us/library/cc236646.aspx
class TargetInfo
# Allowed AvId values for an AV_PAIR
MSV_AV_EOL = "\x00\x00".freeze
MSV_AV_NB_COMPUTER_NAME = "\x01\x00".freeze
MSV_AV_NB_DOMAIN_NAME = "\x02\x00".freeze
MSV_AV_DNS_COMPUTER_NAME = "\x03\x00".freeze
MSV_AV_DNS_DOMAIN_NAME = "\x04\x00".freeze
MSV_AV_DNS_TREE_NAME = "\x05\x00".freeze
MSV_AV_FLAGS = "\x06\x00".freeze
MSV_AV_TIMESTAMP = "\x07\x00".freeze
MSV_AV_SINGLE_HOST = "\x08\x00".freeze
MSV_AV_TARGET_NAME = "\x09\x00".freeze
MSV_AV_CHANNEL_BINDINGS = "\x0A\x00".freeze
# @param av_pair_sequence [String] AV_PAIR list from challenge message
def initialize(av_pair_sequence)
@av_pairs = read_pairs(av_pair_sequence)
end
attr_reader :av_pairs
def to_s
result = ''
av_pairs.each do |k,v|
result << k
result << [v.length].pack('S')
result << v
end
result << Net::NTLM::TargetInfo::MSV_AV_EOL
result << [0].pack('S')
result.force_encoding(Encoding::ASCII_8BIT)
end
private
VALID_PAIR_ID = [
MSV_AV_EOL,
MSV_AV_NB_COMPUTER_NAME,
MSV_AV_NB_DOMAIN_NAME,
MSV_AV_DNS_COMPUTER_NAME,
MSV_AV_DNS_DOMAIN_NAME,
MSV_AV_DNS_TREE_NAME,
MSV_AV_FLAGS,
MSV_AV_TIMESTAMP,
MSV_AV_SINGLE_HOST,
MSV_AV_TARGET_NAME,
MSV_AV_CHANNEL_BINDINGS
].freeze
def read_pairs(av_pair_sequence)
offset = 0
result = {}
return result if av_pair_sequence.nil?
until offset >= av_pair_sequence.length
id = av_pair_sequence[offset..offset+1]
unless VALID_PAIR_ID.include?(id)
raise Net::NTLM::InvalidTargetDataError.new(
"Invalid AvId #{to_hex(id)} in AV_PAIR structure",
av_pair_sequence
)
end
length = av_pair_sequence[offset+2..offset+3].unpack('S')[0].to_i
if length > 0
value = av_pair_sequence[offset+4..offset+4+length-1]
result[id] = value
end
offset += 4 + length
end
result
end
def to_hex(str)
return nil if str.nil?
str.bytes.map {|b| '0x' + b.to_s(16).rjust(2,'0').upcase}.join('-')
end
end
end
end

View File

@ -0,0 +1,11 @@
module Net
module NTLM
# @private
module VERSION
MAJOR = 0
MINOR = 6
TINY = 3
STRING = [MAJOR, MINOR, TINY].join('.')
end
end
end

View File

@ -0,0 +1 @@
require 'net/ntlm'