Created
July 22, 2009 04:26
-
-
Save radar/151807 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'eventmachine' | |
require 'digest/md5' | |
require 'base64' | |
require 'socket' | |
# Dunno, may need this later on. | |
class Session < Struct.new(:username, :id) | |
end | |
SESSIONS = {} | |
module Jabber | |
def domain_name | |
"example.com" | |
end | |
def time_hash | |
Digest::MD5.hexdigest(Time.now.to_f.to_s) | |
end | |
def determine_ip | |
@port, @ip = Socket.unpack_sockaddr_in(get_peername) | |
end | |
def receive_data(data) | |
determine_ip | |
# Client is connecting. | |
if stream = /<stream:stream.*to='(.*?)'/.match(data) | |
send_data("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' from='#{stream[1]}' xml:lang='en' version='1.0'>") | |
# They are trying to log in, send them features. | |
if !(session = SESSIONS[@ip]) | |
send_data("<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>") | |
# They have logged in, and now are starting a new stream. Welcome them. | |
else | |
puts "Welcome aboard, captain." | |
send_data("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='#{session.username}_#{time_hash}' from='example.com' version='1.0'><stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></stream:features>") | |
end | |
# Client is trying to auth... | |
elsif auth = /^<auth(.*?)mechanism='(.*?)'(.*?)\/>/.match(data) | |
if auth[2] == "DIGEST-MD5" | |
@server_nonce = time_hash | |
encodee = "realm=\"talka\",nonce=\"#{@nonce}\",qop=\"auth\",charset=utf-8,algorithm=md5-sess\n" | |
encoded = Base64.encode64(encodee) | |
# Challenge the client to auth correctly. | |
send_data("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> | |
#{encoded} | |
</challenge>") | |
else | |
puts "UNKNOW AUTH PROTOCOL SENT" | |
end | |
# Client sends back response from our auth challenge | |
elsif response = /^<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>(.*?)<\/response>$/.match(data) | |
decoded = Base64.decode64(response[1]) | |
# Gather valuable resources... | |
username = /username="(.*?)"/.match(decoded)[1] | |
nonce = /nonce="(.*?)"/.match(decoded)[1] | |
if nonce = @server_nonce | |
puts "LOGGING IN!" | |
SESSIONS[@ip] = Session.new(username, nonce) | |
# Client responds to challenge and then server tells client they are successfully authenticated. | |
# Even though this isn't a real server we still have to go through the motions. | |
encoded = "rspauth=#{time_hash}" | |
send_data("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>#{encoded}</challenge>") | |
else | |
# I don't know how this would happen, but better to assume it can rather than it can not. | |
puts "BAD NONCE" | |
# Bad nonce | |
send_data("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure></stream:stream>") | |
end | |
# Responding to the next challenge. | |
elsif response = /<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\/>/.match(data) | |
# Welcome aboard, you are now logged in. | |
send_data("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>") | |
# Client asks if it can auth, of course you can! | |
elsif iq = /<iq type='set' id='(\d+)'><query xmlns='jabber:iq:auth'><username>(.*?)<\/username><digest>(.*?)<\/digest><\/query><\/iq>/.match(data) | |
send_data("<iq type='result' id='#{iq[1]}'><query xmlns='jabber:iq:auth'><username>#{iq[2]}<\/username><digest>#{iq[3]}<\/digest><\/query></iq>") | |
# Client asks if it can bind resources, of course you can! | |
elsif iq = /<iq type='set' id='(\d+)'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'\/><\/iq>/.match(data) | |
SESSIONS[ip].id = time_hash[0..7].upcase | |
session = SESSIONS[ip] | |
send_data("<iq type='result' id='#{iq[1]}'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>#{session.username}@#{name}/#{session.id}</jid></bind></iq>") | |
# Client asks if it can have a session, of course you can! | |
elsif iq = /<iq type='set' id='(\d+)'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'\/><\/iq>/.match(data) | |
send_data("<iq type='result' id='#{iq[1]}' />") | |
# Client asks if there's anybody out there... | |
elsif presence = /^<presence\/>$/.match(data) | |
session = SESSIONS[ip] | |
# Since this is not a real server, we'll lie and say there's some people. | |
send_data("<presence from='alice@#{domain}.111A111' to='#{session.username}@#{domain}/#{session.id}'><priority>1</priority></presence>") | |
send_data("<presence from='bobby@#{domain}.222A222' to='#{session.username}@#{domain}/#{session.id}'><priority>1</priority></presence>") | |
else | |
puts "UNKNOWN: #{data}" | |
end | |
end | |
end | |
EM.run { | |
EM.start_server "0.0.0.0", 5222, Jabber | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment