pNet-DNS 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/README +68 -0
  2. data/lib/Net/DNS.rb +879 -0
  3. data/lib/Net/DNS/Header.rb +303 -0
  4. data/lib/Net/DNS/Nameserver.rb +601 -0
  5. data/lib/Net/DNS/Packet.rb +851 -0
  6. data/lib/Net/DNS/Question.rb +117 -0
  7. data/lib/Net/DNS/RR.rb +630 -0
  8. data/lib/Net/DNS/RR/A.rb +103 -0
  9. data/lib/Net/DNS/RR/AAAA.rb +147 -0
  10. data/lib/Net/DNS/RR/AFSDB.rb +114 -0
  11. data/lib/Net/DNS/RR/CERT.rb +191 -0
  12. data/lib/Net/DNS/RR/CNAME.rb +89 -0
  13. data/lib/Net/DNS/RR/DNAME.rb +84 -0
  14. data/lib/Net/DNS/RR/EID.rb +70 -0
  15. data/lib/Net/DNS/RR/HINFO.rb +108 -0
  16. data/lib/Net/DNS/RR/ISDN.rb +118 -0
  17. data/lib/Net/DNS/RR/LOC.rb +341 -0
  18. data/lib/Net/DNS/RR/MB.rb +92 -0
  19. data/lib/Net/DNS/RR/MG.rb +96 -0
  20. data/lib/Net/DNS/RR/MINFO.rb +109 -0
  21. data/lib/Net/DNS/RR/MR.rb +92 -0
  22. data/lib/Net/DNS/RR/MX.rb +124 -0
  23. data/lib/Net/DNS/RR/NAPTR.rb +182 -0
  24. data/lib/Net/DNS/RR/NIMLOC.rb +70 -0
  25. data/lib/Net/DNS/RR/NS.rb +100 -0
  26. data/lib/Net/DNS/RR/NSAP.rb +273 -0
  27. data/lib/Net/DNS/RR/NULL.rb +68 -0
  28. data/lib/Net/DNS/RR/OPT.rb +251 -0
  29. data/lib/Net/DNS/RR/PTR.rb +93 -0
  30. data/lib/Net/DNS/RR/PX.rb +131 -0
  31. data/lib/Net/DNS/RR/RP.rb +108 -0
  32. data/lib/Net/DNS/RR/RT.rb +115 -0
  33. data/lib/Net/DNS/RR/SOA.rb +195 -0
  34. data/lib/Net/DNS/RR/SPF.rb +46 -0
  35. data/lib/Net/DNS/RR/SRV.rb +153 -0
  36. data/lib/Net/DNS/RR/SSHFP.rb +190 -0
  37. data/lib/Net/DNS/RR/TKEY.rb +219 -0
  38. data/lib/Net/DNS/RR/TSIG.rb +358 -0
  39. data/lib/Net/DNS/RR/TXT.rb +162 -0
  40. data/lib/Net/DNS/RR/UNKNOWN.rb +76 -0
  41. data/lib/Net/DNS/RR/X25.rb +90 -0
  42. data/lib/Net/DNS/Resolver.rb +2090 -0
  43. data/lib/Net/DNS/Resolver/Recurse.rb +478 -0
  44. data/lib/Net/DNS/Update.rb +189 -0
  45. data/test/custom.txt +4 -0
  46. data/test/resolv.conf +4 -0
  47. data/test/tc_escapedchars.rb +498 -0
  48. data/test/tc_header.rb +91 -0
  49. data/test/tc_inet6.rb +169 -0
  50. data/test/tc_misc.rb +137 -0
  51. data/test/tc_online.rb +236 -0
  52. data/test/tc_packet.rb +174 -0
  53. data/test/tc_packet_unique_push.rb +126 -0
  54. data/test/tc_question.rb +49 -0
  55. data/test/tc_recurse.rb +69 -0
  56. data/test/tc_res_env.rb +59 -0
  57. data/test/tc_res_file.rb +55 -0
  58. data/test/tc_res_opt.rb +135 -0
  59. data/test/tc_resolver.rb +102 -0
  60. data/test/tc_rr-opt.rb +40 -0
  61. data/test/tc_rr-rrsort.rb +116 -0
  62. data/test/tc_rr-txt.rb +138 -0
  63. data/test/tc_rr-unknown.rb +95 -0
  64. data/test/tc_rr.rb +246 -0
  65. data/test/tc_tcp.rb +34 -0
  66. data/test/tc_tkey.rb +115 -0
  67. data/test/tc_update.rb +226 -0
  68. data/test/ts_netdns.rb +17 -0
  69. data/test/ts_offline.rb +32 -0
  70. data/test/ts_online.rb +33 -0
  71. metadata +119 -0
@@ -0,0 +1,153 @@
1
+ # The contents of this file are subject to the Mozilla
2
+ # Public Licence Version 1.1 (the "Licence"); you may
3
+ # not use this file except in compliance with the
4
+ # Licence. You may obtain a copy of the Licence at
5
+ # http://www.mozilla.org/MPL
6
+ # Software distributed under the Licence is distributed
7
+ # on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
8
+ # either express or implied. See the Licence of the
9
+ # specific language governing rights and limitations
10
+ # under the Licence.
11
+ # The Original Code is pNet::DNS.
12
+ # The Initial Developer of the Original Code is
13
+ # Nominet UK (www.nominet.org.uk). Portions created by
14
+ # Nominet UK are Copyright (c) Nominet UK 2006.
15
+ # All rights reserved.
16
+ module Net
17
+ module DNS
18
+ class RR
19
+ #= NAME
20
+ #
21
+ #Net::DNS::RR::SRV - DNS SRV resource record
22
+ #
23
+ #= DESCRIPTION
24
+ #
25
+ #Class for DNS Service (SRV) resource records.
26
+ #
27
+ #= COPYRIGHT
28
+ #
29
+ #Copyright (c) 1997-2002 Michael Fuhr.
30
+ #
31
+ #Portions Copyright (c) 2002-2004 Chris Reinhardt.
32
+ #
33
+ #Ruby version Copyright (c) 2006 AlexD, Nominet UK.
34
+ #
35
+ #All rights reserved. This program is free software; you may redistribute
36
+ #it and/or modify it under the same terms as Perl itself.
37
+ #
38
+ #= SEE ALSO
39
+ #
40
+ #Net::DNS, Net::DNS::Resolver, Net::DNS::Packet,
41
+ #Net::DNS::Header, Net::DNS::Question, Net::DNS::RR,
42
+ #RFC 2782
43
+ class SRV < RR
44
+ #Returns the priority for this target host.
45
+ #
46
+ # print "priority = ", rr.priority, "\n"
47
+ #
48
+ attr_accessor :priority
49
+ #Returns the weight for this target host.
50
+ #
51
+ # print "weight = ", rr.weight, "\n"
52
+ #
53
+ attr_accessor :weight
54
+ #Returns the port on this target host for the service.
55
+ #
56
+ # print "port = ", rr.port, "\n"
57
+ #
58
+ attr_accessor :port
59
+ #Returns the target host.
60
+ #
61
+ # print "target = ", rr.target, "\n"
62
+ #
63
+ attr_accessor :target
64
+ def init_rrsort_func
65
+ set_rrsort_func("priority", Proc.new { |a,b|
66
+ a.priority <=> b.priority || b.weight <=> a.weight
67
+ })
68
+
69
+
70
+ set_rrsort_func("default_sort", get_rrsort_func("priority"));
71
+
72
+ set_rrsort_func("weight", Proc.new { |a,b|
73
+ b.weight <=> a.weight || a.priority <=> b.priority
74
+ })
75
+ end
76
+
77
+
78
+
79
+ def new_from_data(data, offset)
80
+ if (@rdlength > 0)
81
+ ret = data.unpack("\@#{offset} n3");
82
+ @priority = ret[0]
83
+ @weight = ret[1]
84
+ @port = ret[2]
85
+ offset += 3 * Net::DNS::INT16SZ;
86
+
87
+ @target = Net::DNS::Packet.dn_expand(data, offset)[0];
88
+ end
89
+ end
90
+
91
+ def new_from_hash(values)
92
+ if (values.has_key?:priority)
93
+ @priority = values[:priority]
94
+ end
95
+ if (values.has_key?:weight)
96
+ @weight = values[:weight]
97
+ end
98
+ if (values.has_key?:port)
99
+ @port = values[:port]
100
+ end
101
+ if (values.has_key?:target)
102
+ @target = values[:target]
103
+ end
104
+ end
105
+
106
+ def new_from_string(string)
107
+ if (string && (string =~ /^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)$/))
108
+ @priority = $1.to_i
109
+ @weight = $2.to_i
110
+ @port = $3.to_i
111
+ @target = $4
112
+
113
+ @target.sub!(/\.+$/, "");
114
+ end
115
+ end
116
+
117
+ def rdatastr
118
+ if (defined?@priority)
119
+ rdatastr = [@priority, @weight, @port, @target].join(' ');
120
+ rdatastr.sub!(/(.*[^\.])$/) { |s| s+"." };
121
+ else
122
+ rdatastr = '';
123
+ end
124
+
125
+ return rdatastr;
126
+ end
127
+
128
+ def rr_rdata(packet, offset)
129
+ rdata = '';
130
+
131
+ if (defined?priority)
132
+ rdata += [@priority, @weight, @port].pack('n3');
133
+ rdata += packet.dn_comp(@target, offset + rdata.length );
134
+ end
135
+
136
+ return rdata;
137
+ end
138
+
139
+
140
+ def _canonicalRdata
141
+ rdata = '';
142
+
143
+ if (defined?priority)
144
+ rdata += [@priority.to_i, @weight.to_i, @port.to_i].pack('n3');
145
+ rdata += _name2wire(@target);
146
+ end
147
+
148
+ return rdata;
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,190 @@
1
+ # The contents of this file are subject to the Mozilla
2
+ # Public Licence Version 1.1 (the "Licence"); you may
3
+ # not use this file except in compliance with the
4
+ # Licence. You may obtain a copy of the Licence at
5
+ # http://www.mozilla.org/MPL
6
+ # Software distributed under the Licence is distributed
7
+ # on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
8
+ # either express or implied. See the Licence of the
9
+ # specific language governing rights and limitations
10
+ # under the Licence.
11
+ # The Original Code is pNet::DNS.
12
+ # The Initial Developer of the Original Code is
13
+ # Nominet UK (www.nominet.org.uk). Portions created by
14
+ # Nominet UK are Copyright (c) Nominet UK 2006.
15
+ # All rights reserved.
16
+ module Net
17
+ module DNS
18
+ class RR
19
+ #= NAME
20
+ #
21
+ #Net::DNS::RR::SSHFP - DNS SSHFP resource record
22
+ #
23
+ #= DESCRIPTION
24
+ #
25
+ #Class for Delegation signer (SSHFP) resource records.
26
+ #
27
+ #= ACKNOWLEDGEMENT
28
+ #
29
+ #Jakob Schlyter for code review and supplying patches.
30
+ #
31
+ #= COPYRIGHT
32
+ #
33
+ #Copyright (c) 2004 RIPE NCC, Olaf Kolkman.
34
+ #
35
+ #"All rights reserved, This program is free software; you may redistribute it
36
+ #and/or modify it under the same terms as Perl itself.
37
+ #
38
+ #= SEE ALSO
39
+ #
40
+ #Net::DNS, Net::DNS::Resolver, Net::DNS::Packet,
41
+ #Net::DNS::Header, Net::DNS::Question, Net::DNS::RR,
42
+ #draft-ietf-dnssext-delegation-signer
43
+ class SSHFP < RR
44
+ #Returns the RR's algorithm field in decimal representation
45
+ #
46
+ # 1 = RSA
47
+ # 2 = DSS
48
+ #
49
+ # print "algoritm" = ", rr.algorithm, "\n"
50
+ #
51
+ attr_accessor :algorithm
52
+
53
+ attr_accessor :fingerprint
54
+
55
+ #Returns the SHA1 fingerprint over the label and key in hexadecimal
56
+ #representation.
57
+ #
58
+ #
59
+ #Returns the fingerprint as binary material.
60
+ attr_accessor :fpbin
61
+
62
+ #Returns the fingerprint type of the SSHFP RR.
63
+ #
64
+ # print "fingerprint type = " + rr.fptype + "\n"
65
+ #
66
+ attr_accessor :fptype
67
+ #=head2 babble
68
+ #
69
+ # print $rr->babble;
70
+ #
71
+ #If Digest::BubbleBabble is available on the sytem this method returns the
72
+ #'BabbleBubble' representation of the fingerprint. The 'BabbleBubble'
73
+ #string may be handy for telephone confirmation.
74
+ #
75
+ #The 'BabbleBubble' string returned as a comment behind the RDATA when
76
+ #the string method is called.
77
+ #
78
+ #The method returns an empty string if Digest::BubbleBable is not installed.
79
+ #
80
+
81
+
82
+ # BEGIN {
83
+ # eval {
84
+ # require Digest::BubbleBabble;
85
+ # Digest::BubbleBabble->import(qw(bubblebabble))
86
+ # };
87
+ #
88
+ # $HasBabble = $@ ? 0 : 1;
89
+ #
90
+ # }
91
+ # @TODO Use BubbleBabble!!
92
+ HasBabble = false
93
+
94
+ @@algtype = {
95
+ 'RSA' => 1,
96
+ 'DSA' => 2}
97
+
98
+ @@fingerprinttype = {'SHA-1' => 1}
99
+
100
+ @@fingerprinttypebyval = @@fingerprinttype.reverse
101
+ @@algtypebyval = @@algtype.reverse
102
+
103
+
104
+ def new_from_data(data, offset)
105
+ if (@rdlength > 0)
106
+ offsettoalg = offset
107
+ offsettofptype = offset+1
108
+ offsettofp = offset+2
109
+ fplength = 20 # This will need to change if other fingerprint types
110
+ # are being deployed.
111
+
112
+
113
+ @algorithm = data[oggsettoalg, 1].unpack('C') # , substr($$data, $offsettoalg, 1))
114
+ @fptype = data[offsettofptype, 1].unpack('C') # , substr($$data, $offsettofptype, 1))
115
+
116
+ raise NotImplementedError, "This fingerprint type #{@fptype} has not yet been implemented\n." +
117
+ "Contact developer of Net::DNS::RR::SSHFP.\n" unless fingerprinttypebyval[@fptype] != nil
118
+
119
+ # All this is SHA-1 dependend
120
+ @fpbin = data[offsettofp, fplength] # SHA1 digest 20 bytes long
121
+
122
+ @fingerprint = @fpbin.unpack('H*').upcase! # uc unpack('H*', $self->{:fpbin});
123
+ end
124
+ end
125
+
126
+
127
+ def new_from_string(instring)
128
+ if (string)
129
+ instring = string.tr("()", "") # /()//d
130
+ string.gsub!(/;.*$/, "") # /mg
131
+ string.gsub!(/\n/, "")
132
+
133
+ @algorithm, @fptype, @fingerprint = string.split(/\s+/, 3)
134
+
135
+ # We allow spaces in the fingerprint.
136
+ @fingerprint.gsub!(/\s/, "")
137
+ end
138
+ end
139
+
140
+ def new_from_hash(values)
141
+ if values.has_key?(:fingerprint)
142
+ @fingerprint = values[:fingerprint]
143
+ end
144
+ if values.has_key?(:fptype)
145
+ @fptype = values[:fptype]
146
+ end
147
+ if values.has_key?(:fpbin)
148
+ @fpbin = values[:fpbin]
149
+ end
150
+ if values.has_key?(:algorithm)
151
+ @algorithm = values[:algorithm]
152
+ end
153
+ end
154
+
155
+
156
+ def rdatastr
157
+ rdatastr = ''
158
+
159
+ if (@algorithm!=nil)
160
+ rdatastr = [@algorithm, @fptype, @fingerprint].join(' ') + ' ; ' + babble()
161
+ end
162
+
163
+ return rdatastr
164
+ end
165
+
166
+ def rr_rdata
167
+ if (@algorithm != nil)
168
+ return [@algorithm, @fptype].pack('C2') + fpbin()
169
+ end
170
+
171
+ return ''
172
+ end
173
+
174
+
175
+ def babble
176
+ if (HasBabble)
177
+ return bubblebabble(Digest => fpbin())
178
+ else
179
+ return ""
180
+ end
181
+ end
182
+
183
+
184
+ def fpbin
185
+ return @fpbin ||= [@fingerprint].pack('H*')
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,219 @@
1
+ # The contents of this file are subject to the Mozilla
2
+ # Public Licence Version 1.1 (the "Licence"); you may
3
+ # not use this file except in compliance with the
4
+ # Licence. You may obtain a copy of the Licence at
5
+ # http://www.mozilla.org/MPL
6
+ # Software distributed under the Licence is distributed
7
+ # on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
8
+ # either express or implied. See the Licence of the
9
+ # specific language governing rights and limitations
10
+ # under the Licence.
11
+ # The Original Code is pNet::DNS.
12
+ # The Initial Developer of the Original Code is
13
+ # Nominet UK (www.nominet.org.uk). Portions created by
14
+ # Nominet UK are Copyright (c) Nominet UK 2006.
15
+ # All rights reserved.
16
+ module Net
17
+ module DNS
18
+ class RR
19
+ #= NAME
20
+ #
21
+ #Net::DNS::RR::TKEY - DNS TKEY resource record
22
+ #
23
+ #= DESCRIPTION
24
+ #
25
+ #Class for DNS TKEY resource records.
26
+ #
27
+ #=head1 BUGS
28
+ #
29
+ #This code has not been extensively tested. Use with caution on
30
+ #production systems. See http://samba.org/ftp/samba/tsig-gss/ for an
31
+ #example usage.
32
+ #
33
+ #=head1 COPYRIGHT
34
+ #
35
+ #Copyright (c) 2000 Andrew Tridgell. All rights reserved. This program
36
+ #is free software; you can redistribute it and/or modify it under
37
+ #the same terms as Perl itself.
38
+ #
39
+ #=head1 ACKNOWLEDGMENT
40
+ #
41
+ #The Net::DNS::RR::TKEY module is based on the TSIG module by Michael
42
+ #Fuhr and Chris Turbeville.
43
+ #
44
+ #=head1 SEE ALSO
45
+ #
46
+ #L<perl(1)>, L<Net::DNS>, L<Net::DNS::Resolver>, L<Net::DNS::Packet>,
47
+ #L<Net::DNS::Header>, L<Net::DNS::Question>, L<Net::DNS::RR>,
48
+ #RFC 2845
49
+ class TKEY < RR
50
+ attr_accessor :offset, :key
51
+ #Gets or sets the domain name that specifies the name of the algorithm.
52
+ #The default algorithm is gss.microsoft.com
53
+ #
54
+ # rr.algorithm=(algorithm_name)
55
+ # print "algorithm = ", rr.algorithm, "\n"
56
+ #
57
+ attr_accessor :algorithm
58
+ #Gets or sets the inception time as the number of seconds since 1 Jan 1970
59
+ #00:00:00 UTC.
60
+ #
61
+ #The default inception time is the current time.
62
+ #
63
+ # rr.inception=(time)
64
+ # print "inception = ", rr.inception, "\n"
65
+ #
66
+ attr_accessor :inception
67
+ #Gets or sets the expiration time as the number of seconds since 1 Jan 1970
68
+ #00:00:00 UTC.
69
+ #
70
+ #The default expiration time is the current time plus 1 day.
71
+ #
72
+ # rr.expiration=(time)
73
+ # print "expiration = ", rr.expiration, "\n"
74
+ #
75
+ attr_accessor :expiration
76
+ #Sets the key mode (see rfc2930). The default is 3 which corresponds to GSSAPI
77
+ #
78
+ # rr.mode=(3)
79
+ # print "mode = ", rr.mode, "\n"
80
+ #
81
+ attr_accessor :mode
82
+ #Returns the RCODE covering TKEY processing. See RFC 2930 for details.
83
+ #
84
+ # print "error = ", rr.error, "\n"
85
+ #
86
+ attr_accessor :error
87
+ #Returns the length of the Other Data. Should be zero.
88
+ #
89
+ # print "other len = ", rr.other_len, "\n"
90
+ #
91
+ attr_accessor :other_len
92
+ #Returns the Other Data. This field should be empty.
93
+ #
94
+ # print "other data = ", rr.other_data, "\n"
95
+ #
96
+ attr_accessor :other_data
97
+
98
+ def new_from_data(data, offset)
99
+ # if we have some data then we are parsing an incoming TKEY packet
100
+ # see RFC2930 for the packet format
101
+ if (@rdlength > 0)
102
+ @algorithm, @offset = Net::DNS::Packet::dn_expand(data, offset)
103
+
104
+ @inception, @expiration = data.unpack("\@#{offset} NN")[0]
105
+ offset += Net::DNS::INT32SZ() + Net::DNS::INT32SZ();
106
+
107
+ @inception, @expiration = data.unpack("\@#{offset} nn")[0]
108
+ offset += Net::DNS::INT16SZ + Net::DNS::INT16SZ
109
+
110
+ key_len = data.unpack("\@#{offset} n")[0]
111
+ offset += Net::DNS::INT16SZ
112
+ @key = data[offset, key_len]
113
+ offset += key_len
114
+
115
+ other_len = data.unpack("\@#{offset} n")[0]
116
+ offset += Net::DNS::INT16SZ
117
+ @other_data = data[offset, other_len]
118
+ offset += other_len
119
+ end
120
+ end
121
+
122
+ def new_from_hash(values)
123
+ init_defaults
124
+ if (values.has_key?:key)
125
+ @key = values[:key]
126
+ end
127
+ if (values.has_key?:offset)
128
+ @offset = values[:offset]
129
+ end
130
+ if (values.has_key?:algorithm)
131
+ @algorithm = values[:algorithm]
132
+ end
133
+ if (values.has_key?:expiration)
134
+ @expiration = values[:expiration]
135
+ end
136
+ if (values.has_key?:inception)
137
+ @inception = values[:inception]
138
+ end
139
+ if (values.has_key?:other_len)
140
+ @other_len = values[:other_len]
141
+ end
142
+ if (values.has_key?:other_data)
143
+ @other_data = values[:other_data]
144
+ end
145
+ if (values.has_key?:error)
146
+ @error = values[:error]
147
+ end
148
+ if (values.has_key?:mode)
149
+ @mode = values[:mode]
150
+ end
151
+ end
152
+
153
+ def new_from_string(string)
154
+ if (string != nil && (string =~ /^(.*)$/))
155
+ @key = $1;
156
+ end
157
+
158
+ init_defaults
159
+ end
160
+
161
+ def init_defaults
162
+ @algorithm = "gss.microsoft.com"
163
+ @inception = Time.now
164
+ @expiration = Time.now + 24*60*60
165
+ @mode = 3 # GSSAPI
166
+ @error = 0
167
+ @other_len = 0
168
+ @other_data = ""
169
+ end
170
+
171
+ def error
172
+ rcode=0
173
+ error = @error
174
+
175
+ if (error!=nil)
176
+ rcode = Net::DNS::rcodesbyval[error] || error
177
+ end
178
+
179
+ return rcode
180
+ end
181
+
182
+ def rdatastr
183
+ error = @error
184
+ error = "UNDEFINED" unless error!=nil
185
+
186
+ rdatastr=""
187
+
188
+ if (@algorithm!=nil)
189
+ rdatastr = "#{@algorithm}. #{error}"
190
+ if (@other_len != nil && @other_len >0 && @other_data!=nil)
191
+ rdatastr += " #{@other_data}"
192
+ end
193
+ else
194
+ rdatastr = ''
195
+ end
196
+
197
+ return rdatastr
198
+ end
199
+
200
+ def rr_rdata(packet, offset)
201
+ rdata = ""
202
+
203
+ packet.compnames = Hash.new()
204
+ rdata += packet.dn_comp(@algorithm, 0)
205
+ rdata += [@inception].pack("N")
206
+ rdata += [@expiration].pack("N")
207
+ rdata += [@mode].pack("n")
208
+ rdata += [0].pack("n"); # error
209
+ rdata += [@key.length].pack("n")
210
+ rdata += @key
211
+ rdata += [@other_data.length].pack("n")
212
+ rdata += @other_data
213
+
214
+ return rdata
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end