Class: Cinch::DCC::Incoming::Send
- Inherits:
-
Object
- Object
- Cinch::DCC::Incoming::Send
- Defined in:
- lib/cinch/dcc/incoming/send.rb
Overview
DCC SEND is a protocol for transferring files, usually found in IRC. While the handshake, i.e. the details of the file transfer, are transferred over IRC, the actual file transfer happens directly between two clients. As such it doesn’t put stress on the IRC server.
When someone tries to send a file to the bot, the ‘:dcc_send` event will be triggered, in which the DCC request can be inspected and optionally accepted.
The event handler receives the plain message object as well as an instance of this class. That instance contains information about the suggested file name (in a sanitized way) and allows for checking the origin.
It is advised to reject transfers that seem to originate from a private IP or the local IP itself unless that is expected. Otherwise, specially crafted requests could cause the bot to connect to internal services.
Finally, the file transfer can be accepted and written to any object that implements a ‘#<<` method, which includes File objects as well as plain strings.
Constant Summary collapse
- PRIVATE_NETS =
[IPAddr.new("fc00::/7"), IPAddr.new("10.0.0.0/8"), IPAddr.new("172.16.0.0/12"), IPAddr.new("192.168.0.0/16")]
- LOCAL_NETS =
[IPAddr.new("127.0.0.0/8"), IPAddr.new("::1/128")]
Instance Attribute Summary collapse
-
#filename ⇒ String
readonly
The basename of the file name, with (back)slashes removed.
- #ip ⇒ String readonly
- #port ⇒ Fixnum readonly
- #size ⇒ Integer readonly
- #user ⇒ User readonly
Instance Method Summary collapse
-
#accept(io) ⇒ Boolean
This method is used for accepting a DCC SEND offer.
-
#from_localhost? ⇒ Boolean
True if the DCC originates from localhost.
-
#from_private_ip? ⇒ Boolean
True if the DCC originates from a private ip.
-
#initialize(opts) ⇒ Send
constructor
private
A new instance of Send.
Constructor Details
#initialize(opts) ⇒ Send
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of Send.
80 81 82 |
# File 'lib/cinch/dcc/incoming/send.rb', line 80 def initialize(opts) @user, @filename, @size, @ip, @port = opts.values_at(:user, :filename, :size, :ip, :port) end |
Instance Attribute Details
#filename ⇒ String (readonly)
Returns The basename of the file name, with (back)slashes removed.
50 51 52 |
# File 'lib/cinch/dcc/incoming/send.rb', line 50 def filename @filename end |
#port ⇒ Fixnum (readonly)
71 72 73 |
# File 'lib/cinch/dcc/incoming/send.rb', line 71 def port @port end |
#size ⇒ Integer (readonly)
65 66 67 |
# File 'lib/cinch/dcc/incoming/send.rb', line 65 def size @size end |
#user ⇒ User (readonly)
62 63 64 |
# File 'lib/cinch/dcc/incoming/send.rb', line 62 def user @user end |
Instance Method Details
#accept(io) ⇒ Boolean
This method blocks.
This method is used for accepting a DCC SEND offer. It expects an object to save the result to (usually an instance of IO or String).
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/cinch/dcc/incoming/send.rb', line 106 def accept(io) socket = TCPSocket.new(@ip, @port) total = 0 while (buf = socket.readpartial(8192)) total += buf.bytesize begin socket.write_nonblock [total].pack("N") rescue Errno::EWOULDBLOCK, Errno::AGAIN # Nobody cares about ACKs, really. And if the sender # couldn't receive it at this point, they probably don't # care, either. end io << buf # Break here in case the sender doesn't close the # connection on the final ACK. break if total == @size end socket.close true rescue EOFError false end |
#from_localhost? ⇒ Boolean
Returns True if the DCC originates from localhost.
142 143 144 145 |
# File 'lib/cinch/dcc/incoming/send.rb', line 142 def from_localhost? ip = IPAddr.new(@ip) LOCAL_NETS.any? { |n| n.include?(ip) } end |
#from_private_ip? ⇒ Boolean
Returns True if the DCC originates from a private ip.
135 136 137 138 |
# File 'lib/cinch/dcc/incoming/send.rb', line 135 def from_private_ip? ip = IPAddr.new(@ip) PRIVATE_NETS.any? { |n| n.include?(ip) } end |