Upload
hayden-clayton
View
216
Download
0
Embed Size (px)
Citation preview
Implementierung eines IRC Servers in Java
von
M. Serhat Cinar AI 11030409
WPF Compiler & Interpreter SS04
bei Prof. Dr. Ehses
2
IRC
IRC := Internet Relay Chat (Relay = Relais, Übertragung)
Der Benutzer meldet sich bei einem Server (z.B. A) an.Dieser Server wiederum ist mit anderen Servern desselben Netzes
verbunden (z.B. mit Server C über Server B).Jeder Benutzer, der mit einem Server verbunden ist erhält vom Server
eine Liste aller Channels, die in dem jeweiligen Netz verfügbar sind und kann sich in jedem beliebigen Channel anmelden (JOIN).
Das IRC-Geflecht von Servern ist in Netze aufgeteilt. Beispiele für verschiedene große Netze sind das DALnet, Efnet oder IRCnet.
Benutzer im selben Channel können sich unterhalten (chatten). Schickt Benutzer A über Server A eine Nachricht an Benutzer C an
Server C, so wird die Nachricht wie ein PING über das IRC-Netz geschickt. Die verschiedenen Stationen bei einer solchen Weiterleitung nennt man Relais.
Das Prinzip ist dem DNS, Mail-Relay oder dem PING Protokoll ähnlich.
3
IRC als Netzwerk
4
IRC - Standard
Die Standards für das IRC-Protokoll werden in den Request for Comments (RFC‘s) verwaltet.
RFC 1459 - IRC ProtocolRFC 2810 - IRC ArchitectureRFC 2811 - IRC Channel ManagementRFC 2812 - IRC Client ProtocolRFC 2813 - IRC Server Protocol
5
RFC 2812 Internet Relay Chat: Client Protocol 1/2
message = [ ":" prefix SPACE ] command [ params ] crlf
prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
command = 1*letter / 3digit
params = *14( SPACE middle ) [ SPACE ":" trailing ]
=/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF
; any octet except NUL, CR, LF, " " and ":"
middle = nospcrlfcl *( ":" / nospcrlfcl )
trailing = *( ":" / " " / nospcrlfcl )
SPACE = %x20 ; space character
crlf = %x0D %x0A ; "carriage return" "linefeed"
letter = %x41-5A / %x61-7A ; A-Z / a-z
digit = %x30-39 ; 0-9
hexdigit = digit / "A" / "B" / "C" / "D" / "E" / "F"
special = %x5B-60 / %x7B-7D
; "[", "]", "\", "`", "_", "^", "{", "|", "}„
user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
; any octet except NUL, CR, LF, " " and "@"
6
RFC 2812 Internet Relay Chat: Client Protocol 2/2
key = 1*23( %x01-05 / %x07-08 / %x0C / %x0E-1F / %x21-7F )
; any 7-bit US_ASCII character,
; except NUL, CR, LF, FF, h/v TABs, and " "
servername = hostname
host = hostname / hostaddr
hostname = shortname *( "." shortname )
shortname = ( letter / digit ) *( letter / digit / "-" )
*( letter / digit )
; as specified in RFC 1123 [HNAME]
hostaddr = ip4addr / ip6addr
ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
ip6addr = 1*hexdigit 7( ":" 1*hexdigit )
ip6addr =/ "0:0:0:0:0:" ( "0" / "FFFF" ) ":" ip4addr
nickname = ( letter / special ) *8( letter / digit / special / "-" )
7
Message Grundstruktur
Message
:Prefix Command Paramlist
:TrailingSPACE Param *Server / User
8
Verschiedene Implementierungen: CRLF vs. LF
RFC 2812:message = [ ":" prefix SPACE ] command [ params ]
crlfcrlf = %x0D %x0A
mIRC:4E 49 43 4B 20 74 65 73 74 0A NICK
test.
BitchX:4E 49 43 4B 20 74 65 73 74 0D 0A NICK
test..
9
jIrcSX.flex 1/2
CR = \x0D
LF = \x0A
CRLF = {CR} {LF}
SPACE = \x20
DIGIT = \x30|\x31|\x32|\x33|\x34|\x35|\x36|\x37|\x38|\x39
LETTER_HEX = "A" | "B" | "C" | "D" | "E" | "F"
LETTER = [A-Za-z]
COLON = ":"
AT = "@"
EXCLAMATION_MARK = "!"
DOT = "."
VALUE = "#"
COMMA = ","
PLUS = "+"
MINUS = "-"
AND = "&"
10
jIrcSX.flex 2/2
UNDERSCORE = "_"ACCENT_CIRCONFLEX = "^"SQUARED_BRACKET_LEFT = "["SQUARED_BRACKET_RIGHT = "]"BRACE_LEFT = "{"BRACE_RIGHT = "}"BACKSLASH = "\\"APOSTROPHE = "`"OR = "|"TAB = \x09NULL = \x00BELL = \x07FF = \x0CSYMBOL_OTHER = .
HEXDIGIT = {DIGIT} | {LETTER_HEX}IP6ADDR = ( {HEXDIGIT}+ (":" {HEXDIGIT}+){7} ) | ( "0:0:0:0:0:" ("0"|"FFFF") ":" {IP4ADDR} )IP4ADDR = {DIGIT} {1,3} "." {DIGIT} {1,3} "." {DIGIT} {1,3} "." {DIGIT} {1,3}
HOSTNAME = {SHORTNAME} ( "." {SHORTNAME})+SHORTNAME = ( {LETTER} | {DIGIT} ) ( {LETTER} | {DIGIT} | "-" )*
( {LETTER} | {DIGIT} )*
11
Recursive Descending Parser
private int lookahead(), private void match(int symbol), private void match()public INode parse(String pIrcMessage)private INode message()private INode prefix()private String nickname()private String letterOrSpecial()private String letterOrSpecialOrDigitOrMinusOrEmpty()private String user()private String userLetterOrEmpty()private INode host()private INode hostaddr()private INode command()private INode params()private String middle()private String middleRepeatOrEmpty()private String trailing()
12
MessageNode
public class MessageNode implements INode {public INode mPrefix, mCommand, mParams;public void accept(IVisitor v)[...] public String getCommand(){
return ((CommandNode) mCommand).mCommand;}public boolean hasColonedTrailing(){
if (mParams==null) return false;return ((ParamsNode) mParams).mColonedTrailing;
}public int getParamsSize(){
if (mParams==null) return 0;return ((ParamsNode) mParams).mMiddles.size();
}public String getParam(int i){
if (mParams==null) return null;ParamsNode lPN = (ParamsNode) mParams;if (lPN.mMiddles == null || lPN.mMiddles.size()<=i) return
null;return ((String)lPN.mMiddles.get(i));
}}
13
Weitere Nodes 1/2
public class PrefixHostNameNode implements INode {
public String mServerName;
[...]
}
public class CommandNode implements INode {
public String mCommand;
[...]
}
public class PrefixNickUserHostNode implements INode {
public String mNickName, mUser;
public INode mHost;
[...]
}
14
Weitere Nodes 2/2
public class IPv4AdressNode implements INode {
public String mAdress;
[...]
}
public class ParamsNode implements INode {
public Vector mMiddles;
public boolean mColonedTrailing = false;
}
15
Verbindungsaufbau Client->Server 1/2
The commands described here are used to register a connection with an IRC server as
a user as well as to correctly disconnect.
A "PASS" command is not required for a client connection to be registered, but it MUST
precede the latter of the NICK/USER combination (for a user connection) or the SERVICE command (for a service connection). The RECOMMENDED order for a client to register is as follows:
1. Pass message 2. Nick message 2. Service message 3. User message
Upon success, the client will receive an RPL_WELCOME (for users) or RPL_YOURESERVICE (for services) message indicating that the connection is
now registered and known the to the entire IRC network.The reply message MUST contain the full client identifier upon which it was
registered.
16
Verbindungsaufbau Client->Server 2/2
NICK test
USER test "" "irc.gamesnet.net" :x
:genesis.GamesNET.net NOTICE AUTH :Looking up the hostname for 81.173.149.151...
:genesis.GamesNET.net NOTICE AUTH :Please wait while your system is scanned for insecure proxy servers...
:genesis.GamesNET.net NOTICE AUTH :Successfully resolved your IP to xdsl-81-173-149-151.netcologne.de.
PING :4048004E
PING :4048004E
PONG :4048004E
PONG :4048004E
:genesis.GamesNET.net 001 test :Welcome to the Internet Relay Chat network, [email protected]
= Client = Server
17
NICK Message
Command: NICK Parameters: <nickname>
NICK command is used to give user a nickname or change the existing one.
Numeric Replies: ERR_NONICKNAMEGIVEN ERR_ERRONEUSNICKNAME ERR_NICKNAMEINUSE ERR_NICKCOLLISION ERR_UNAVAILRESOURCE ERR_RESTRICTED
Examples: NICK Wiz ; Introducing new nick "Wiz" if session is still
unregistered, or user changing his nickname to "Wiz"
:[email protected] NICK Kilroy ;Server telling that WiZ changed his nickname to Kilroy.
18
Weitere Befehle 1/3
3.1 Connection Registration3.1.1 Password message3.1.2 Nick message3.1.3 User message3.1.4 Oper message3.1.5 User mode message3.1.6 Service message3.1.7 Quit3.1.8 Squit
3.2 Channel operations3.2.1 Join message3.2.2 Part message3.2.3 Channel mode message3.2.4 Topic message3.2.5 Names message3.2.6 List message3.2.7 Invite message3.2.8 Kick command
19
Weitere Befehle 2/3
3.3 Sending messages3.3.1 Private messages3.3.2 Notice
3.4 Server queries and commands3.4.1 Motd message3.4.2 Lusers message3.4.3 Version message3.4.4 Stats message3.4.5 Links message3.4.6 Time message3.4.7 Connect message3.4.8 Trace message3.4.9 Admin command3.4.10 Info command
3.5 Service Query and Commands 3.5.1 Servlist message 3.5.2 Squery
20
Weitere Befehle 3/3
3.6 User based queries 3.6.1 Who query 3.6.2 Whois query 3.6.3 Whowas3.7 Miscellaneous messages 3.7.1 Kill message 3.7.2 Ping message 3.7.3 Pong message 3.7.4 Error4. Optional features 4.1 Away 4.2 Rehash message 4.3 Die message 4.4 Restart message 4.5 Summon message 4.6 Users 4.7 Operwall message 4.8 Userhost message 4.9 Ison message
21
Software Architektur
.
Server ClientHandler
1 *
RDParser
11
RegistrationProcessor
CommandProcessor
1 11 44
.
.
User1 1
UserList
11
1
*
22
User
Die Klasse User verwaltet userbezogene Informationen.
public class User {
public String mNick;
public ClientHandler mClientHandler;
public String mPass;
public String mUsername;
public String mHostname;
public String mRealname;
public String mMode;
public MessageNode mLastMessage;
public String mInternetAdress;
}
23
Server
Der Server erzeugt einen ServerSocket auf dem IRC Port 6667. Für jede ankommende Verbindung wird ein eigener ClientHandler erzeugt.
while (mRunning){new ClientHandler(lServerSocket.accept(), this);
}
Weiterhin stellt der Server Zentrale Informationen zur Verfügung, wie die Liste der aktuell verbundenen Clients.
public String getName()public String getServerAdress()public String getServerAdressString()public int getServerPort()public UserList getUserList()
24
ClientHandler 1/3
Jeder Client erhält einen eigenen ClientHandler, der als nebenläufiger Prozess alle Eingabezeichen des Clients aus dem InputStream des Sockets liest, bis das Ende einer Message erreicht wird und anschließend parst.
public synchronized MessageNode getNextMessageNode() throws SyntaxException, IOException{StringBuffer lInputBuffer = new StringBuffer();int lReadByte;while ( (lReadByte = mInputReader.read()) != -1){
lInputBuffer.append((char)lReadByte);if (lInputBuffer.indexOf(LexicalSymbols.STRING_LF)>-1)
break;}if (lReadByte==-1) mUser.mLastMessage = null;else mUser.mLastMessage = (MessageNode) mServer.parse(lInputBuffer.toString());return mUser.mLastMessage;
}
25
ClientHandler 2/3
Weitere Methoden ermöglichen das Senden von Nachrichten über den OutputSteam:
public synchronized void sendMessage(String pMessage){
Log.getInstance().writeln(toString()+" sending: "+pMessage);
mPrintWriter.print(pMessage+LexicalSymbols.STRING_CRLF);
mPrintWriter.flush();
}
public synchronized void sendNumericReply(String [] pNumber){
sendMessage(NumericReplies.getNumericReplyMessage(pNumber, mUser));
}
26
ClientHandler 3/3
try {RegistrationProcessor lRegistration = new RegistrationProcessor();lRegistration.process(mUser, null);while (mRunning){
// read next message from clienttry {
lMessage = getNextMessageNode(); if (lMessage==null)mRunning = false; if
(mCommandProcessors.containsKey(lMessage.getCommand().toUpperCase())){
lCommandProcessor = (ICommandProcessor) mCommandProcessors.get(lMessage.getCommand().toUpperCase());
lCommandProcessor.process(mUser, lMessage);}
}catch (SyntaxException e) {
Log.getInstance().writeln("ParseException: "+e.toString());sendNumericReply(NumericReplies.ERR_NEEDMOREPARAMS);
}
27
ICommandProcessor
public interface ICommandProcessor {public void process(User pUser, MessageNode pMessageNode)
throws IOException;
public String getCommandString();}
28
NickProcessor
public void process(User pUser, MessageNode pMessageNode)throws IOException {
String lNewNick = pMessageNode.getParam(0); if (lNewNick != null){
UserList lList = pUser.mClientHandler.getServer().getUserList();
lList.renameUser(pUser, lNewNick);// TODO broadcast new nick of user
}else pUser.mClientHandler.
sendNumericReply(NumericReplies.ERR_NONICKNAMEGIVEN);
}
public String getCommandString() {return CommandStrings.NICK;
}
29
PingProcessor
public void process(User pUser, MessageNode pMessageNode)
throws IOException {
// PING :4048004E
// PONG :4048004E
String lPingCode = pMessageNode.getParam(0);
if (lPingCode != null)
pUser.mClientHandler.sendMessage("PONG :"+lPingCode);
else pUser.mClientHandler.sendMessage("PONG");
}
public String getCommandString() {
return CommandStrings.PING;
}
30
Auszug einer Login-Prozedur 1/2
Client connected: 127.0.0.1
ClientHandler 127.0.0.1 received: "NICK test"
COMMAND: NICK
MIDDLE: test
ClientHandler test received: "USER test "" "127.0.0.1" :x"
COMMAND: USER
MIDDLE: test
MIDDLE: ""
MIDDLE: "127.0.0.1"
ClientHandler test sending: :127.0.0.1 001 test :Welcome to the Internet Relay Network [email protected]
ClientHandler test sending: :127.0.0.1 002 test :Your host is 127.0.0.1 [127.0.0.1:6667], running version jIRC Server X Version 25.06.2004
ClientHandler test sending: :127.0.0.1 003 test :This server was created <date>
31
Auszug einer Login-Prozedur 2/2
ClientHandler test sending: :127.0.0.1 004 test :127.0.0.1 [127.0.0.1:6667] <version> <available user modes> <available channel modes>
ClientHandler test sending: :127.0.0.1 005 test :Try server 127.0.0.1, port <port number>
ClientHandler test sending: :127.0.0.1 251 test :There are <integer> users and <integer> services on <integer> servers
ClientHandler test sending: :127.0.0.1 252 test <integer> :operator(s) online
ClientHandler test sending: :127.0.0.1 253 test <integer> :unknown connection(s)
ClientHandler test sending: :127.0.0.1 254 test <integer> :channels formed
ClientHandler test sending: :127.0.0.1 255 test :I have <integer> clients and <integer> servers
ClientHandler test sending: :127.0.0.1 375 test :- <server> Message of the day -
ClientHandler test sending: :127.0.0.1 372 test :- <text>ClientHandler test sending: :127.0.0.1 376 test :End of MOTD
command
32
TODO
• Weitere IRC Befehle über die Schnittstelle ICommandProcessor implementieren
• Channel Verwaltung• Server to Server Kommunikation
33
Referenzen
jIrcSX – www.graviton.de/ai/c-und-iDer in diesem Projekt entstehende IRC-Server
BitchX – www.bitchx.orgDOS IRC Client für Unix/Linux/MacOS/Windows
mIRC - www.mirc.comIRC Client mit GUI für Windows
RFC Archiv - www.faqs.org/rfcs
jFlex – jflex.deJava Scanner Generator