Blob


1 /*
2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4 *
5 * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6 * der GNU General Public License (GPL), wie von der Free Software Foundation
7 * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8 * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9 * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10 * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11 *
12 * $Id: parse.c,v 1.41 2002/09/16 09:20:27 alex Exp $
13 *
14 * parse.c: Parsen der Client-Anfragen
15 */
18 #include "portab.h"
20 #include "imp.h"
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
26 #include "ngircd.h"
27 #include "defines.h"
28 #include "conn.h"
29 #include "client.h"
30 #include "channel.h"
31 #include "log.h"
32 #include "messages.h"
33 #include "tool.h"
35 #include "exp.h"
36 #include "parse.h"
38 #include "imp.h"
39 #include "irc.h"
40 #include "irc-channel.h"
41 #include "irc-login.h"
42 #include "irc-mode.h"
43 #include "irc-op.h"
44 #include "irc-oper.h"
45 #include "irc-server.h"
46 #include "irc-write.h"
48 #include "exp.h"
51 LOCAL VOID Init_Request PARAMS(( REQUEST *Req ));
53 LOCAL BOOLEAN Validate_Prefix PARAMS(( CONN_ID Idx, REQUEST *Req, BOOLEAN *Closed ));
54 LOCAL BOOLEAN Validate_Command PARAMS(( CONN_ID Idx, REQUEST *Req, BOOLEAN *Closed ));
55 LOCAL BOOLEAN Validate_Args PARAMS(( CONN_ID Idx, REQUEST *Req, BOOLEAN *Closed ));
57 LOCAL BOOLEAN Handle_Request PARAMS(( CONN_ID Idx, REQUEST *Req ));
60 GLOBAL BOOLEAN
61 Parse_Request( CONN_ID Idx, CHAR *Request )
62 {
63 /* Client-Request parsen. Bei einem schwerwiegenden Fehler wird
64 * die Verbindung geschlossen und FALSE geliefert.
65 * Der Aufbau gueltiger Requests ist in RFC 2812, 2.3 definiert. */
67 REQUEST req;
68 CHAR *start, *ptr;
69 BOOLEAN closed;
71 assert( Idx >= 0 );
72 assert( Request != NULL );
74 #ifdef SNIFFER
75 if( NGIRCd_Sniffer ) Log( LOG_DEBUG, " <- connection %d: '%s'.", Idx, Request );
76 #endif
78 Init_Request( &req );
80 /* Fuehrendes und folgendes "Geraffel" verwerfen */
81 ngt_TrimStr( Request );
83 /* gibt es ein Prefix? */
84 if( Request[0] == ':' )
85 {
86 /* Prefix vorhanden */
87 req.prefix = Request + 1;
88 ptr = strchr( Request, ' ' );
89 if( ! ptr )
90 {
91 Log( LOG_DEBUG, "Connection %d: Parse error: prefix without command!?", Idx );
92 return Conn_WriteStr( Idx, "ERROR :Prefix without command!?" );
93 }
94 *ptr = '\0';
95 #ifndef STRICT_RFC
96 /* multiple Leerzeichen als Trenner zwischen
97 * Prefix und Befehl ignorieren */
98 while( *(ptr + 1) == ' ' ) ptr++;
99 #endif
100 start = ptr + 1;
102 else start = Request;
104 if( ! Validate_Prefix( Idx, &req, &closed )) return ! closed;
106 /* Befehl */
107 ptr = strchr( start, ' ' );
108 if( ptr )
110 *ptr = '\0';
111 #ifndef STRICT_RFC
112 /* multiple Leerzeichen als Trenner vor
113 *Parametertrennern ignorieren */
114 while( *(ptr + 1) == ' ' ) ptr++;
115 #endif
117 req.command = start;
119 if( ! Validate_Command( Idx, &req, &closed )) return ! closed;
121 /* Argumente, Parameter */
122 if( ptr )
124 /* Prinzipiell gibt es welche :-) */
125 start = ptr + 1;
126 while( start )
128 /* Parameter-String "zerlegen" */
129 if( start[0] == ':' )
131 req.argv[req.argc] = start + 1;
132 ptr = NULL;
134 else
136 req.argv[req.argc] = start;
137 ptr = strchr( start, ' ' );
138 if( ptr )
140 *ptr = '\0';
141 #ifndef STRICT_RFC
142 /* multiple Leerzeichen als
143 * Parametertrenner ignorieren */
144 while( *(ptr + 1) == ' ' ) ptr++;
145 #endif
149 req.argc++;
151 if( start[0] == ':' ) break;
152 if( req.argc > 14 ) break;
154 if( ptr ) start = ptr + 1;
155 else start = NULL;
159 if( ! Validate_Args( Idx, &req, &closed )) return ! closed;
161 return Handle_Request( Idx, &req );
162 } /* Parse_Request */
165 LOCAL VOID
166 Init_Request( REQUEST *Req )
168 /* Neue Request-Struktur initialisieren */
170 INT i;
172 assert( Req != NULL );
174 Req->prefix = NULL;
175 Req->command = NULL;
176 for( i = 0; i < 15; Req->argv[i++] = NULL );
177 Req->argc = 0;
178 } /* Init_Request */
181 LOCAL BOOLEAN
182 Validate_Prefix( CONN_ID Idx, REQUEST *Req, BOOLEAN *Closed )
184 CLIENT *client, *c;
186 assert( Idx >= 0 );
187 assert( Req != NULL );
189 *Closed = FALSE;
191 /* ist ueberhaupt ein Prefix vorhanden? */
192 if( ! Req->prefix ) return TRUE;
194 /* Client-Struktur der Connection ermitteln */
195 client = Client_GetFromConn( Idx );
196 assert( client != NULL );
198 /* nur validieren, wenn bereits registrierte Verbindung */
199 if(( Client_Type( client ) != CLIENT_USER ) && ( Client_Type( client ) != CLIENT_SERVER ) && ( Client_Type( client ) != CLIENT_SERVICE ))
201 /* noch nicht registrierte Verbindung.
202 * Das Prefix wird ignoriert. */
203 Req->prefix = NULL;
204 return TRUE;
207 /* pruefen, ob der im Prefix angegebene Client bekannt ist */
208 c = Client_Search( Req->prefix );
209 if( ! c )
211 /* im Prefix angegebener Client ist nicht bekannt */
212 Log( LOG_ERR, "Invalid prefix, client not known (connection %d)!?", Idx );
213 if( ! Conn_WriteStr( Idx, "ERROR :Invalid prefix, client not known!?" )) *Closed = TRUE;
214 return FALSE;
217 /* pruefen, ob der Client mit dem angegebenen Prefix in Richtung
218 * des Senders liegt, d.h. sicherstellen, dass das Prefix nicht
219 * gefaelscht ist */
220 if( Client_NextHop( c ) != client )
222 /* das angegebene Prefix ist aus dieser Richtung, also
223 * aus der gegebenen Connection, ungueltig! */
224 Log( LOG_ERR, "Spoofed prefix \"%s\" from \"%s\" (connection %d)!", Req->prefix, Client_Mask( Client_GetFromConn( Idx )), Idx );
225 Conn_Close( Idx, NULL, "Spoofed prefix", TRUE );
226 *Closed = TRUE;
227 return FALSE;
230 return TRUE;
231 } /* Validate_Prefix */
234 LOCAL BOOLEAN
235 Validate_Command( CONN_ID Idx, REQUEST *Req, BOOLEAN *Closed )
237 assert( Idx >= 0 );
238 assert( Req != NULL );
239 *Closed = FALSE;
241 return TRUE;
242 } /* Validate_Comman */
245 LOCAL BOOLEAN
246 Validate_Args( CONN_ID Idx, REQUEST *Req, BOOLEAN *Closed )
248 assert( Idx >= 0 );
249 assert( Req != NULL );
250 *Closed = FALSE;
252 return TRUE;
253 } /* Validate_Args */
256 LOCAL BOOLEAN
257 Handle_Request( CONN_ID Idx, REQUEST *Req )
259 /* Client-Request verarbeiten. Bei einem schwerwiegenden Fehler
260 * wird die Verbindung geschlossen und FALSE geliefert. */
262 CLIENT *client, *target, *prefix;
263 CHAR str[LINE_LEN];
264 INT i;
266 assert( Idx >= 0 );
267 assert( Req != NULL );
268 assert( Req->command != NULL );
270 client = Client_GetFromConn( Idx );
271 assert( client != NULL );
273 /* Statuscode, der geforwarded werden muss? */
274 if(( strlen( Req->command ) == 3 ) && ( atoi( Req->command ) > 100 ))
276 /* Befehl ist ein Statuscode */
278 /* Zielserver ermitteln */
279 if(( Client_Type( client ) == CLIENT_SERVER ) && ( Req->argc > 0 )) target = Client_Search( Req->argv[0] );
280 else target = NULL;
281 if( ! target )
283 if( Req->argc > 0 ) Log( LOG_WARNING, "Unknown target for status code: \"%s\"", Req->argv[0] );
284 else Log( LOG_WARNING, "Unknown target for status code!" );
285 return TRUE;
287 if( target == Client_ThisServer( ))
289 Log( LOG_DEBUG, "Ignored status code %s from \"%s\".", Req->command, Client_ID( client ));
290 return TRUE;
293 /* Quell-Client ermitteln */
294 if( ! Req->prefix[0] )
296 Log( LOG_WARNING, "Got status code without prefix!?" );
297 return TRUE;
299 else prefix = Client_Search( Req->prefix );
300 if( ! prefix )
302 Log( LOG_WARNING, "Got status code from unknown source: \"%s\"", Req->prefix );
303 return TRUE;
306 /* Statuscode weiterleiten */
307 strcpy( str, Req->command );
308 for( i = 0; i < Req->argc; i++ )
310 if( i < Req->argc - 1 ) strcat( str, " " );
311 else strcat( str, " :" );
312 strcat( str, Req->argv[i] );
314 return IRC_WriteStrClientPrefix( target, prefix, str );
317 if( strcasecmp( Req->command, "PASS" ) == 0 ) return IRC_PASS( client, Req );
318 else if( strcasecmp( Req->command, "NICK" ) == 0 ) return IRC_NICK( client, Req );
319 else if( strcasecmp( Req->command, "USER" ) == 0 ) return IRC_USER( client, Req );
320 else if( strcasecmp( Req->command, "SERVER" ) == 0 ) return IRC_SERVER( client, Req );
321 else if( strcasecmp( Req->command, "NJOIN" ) == 0 ) return IRC_NJOIN( client, Req );
322 else if( strcasecmp( Req->command, "QUIT" ) == 0 ) return IRC_QUIT( client, Req );
323 else if( strcasecmp( Req->command, "SQUIT" ) == 0 ) return IRC_SQUIT( client, Req );
324 else if( strcasecmp( Req->command, "PING" ) == 0 ) return IRC_PING( client, Req );
325 else if( strcasecmp( Req->command, "PONG" ) == 0 ) return IRC_PONG( client, Req );
326 else if( strcasecmp( Req->command, "MOTD" ) == 0 ) return IRC_MOTD( client, Req );
327 else if( strcasecmp( Req->command, "PRIVMSG" ) == 0 ) return IRC_PRIVMSG( client, Req );
328 else if( strcasecmp( Req->command, "NOTICE" ) == 0 ) return IRC_NOTICE( client, Req );
329 else if( strcasecmp( Req->command, "MODE" ) == 0 ) return IRC_MODE( client, Req );
330 else if( strcasecmp( Req->command, "NAMES" ) == 0 ) return IRC_NAMES( client, Req );
331 else if( strcasecmp( Req->command, "ISON" ) == 0 ) return IRC_ISON( client, Req );
332 else if( strcasecmp( Req->command, "WHOIS" ) == 0 ) return IRC_WHOIS( client, Req );
333 else if( strcasecmp( Req->command, "USERHOST" ) == 0 ) return IRC_USERHOST( client, Req );
334 else if( strcasecmp( Req->command, "OPER" ) == 0 ) return IRC_OPER( client, Req );
335 else if( strcasecmp( Req->command, "DIE" ) == 0 ) return IRC_DIE( client, Req );
336 else if( strcasecmp( Req->command, "RESTART" ) == 0 ) return IRC_RESTART( client, Req );
337 else if( strcasecmp( Req->command, "ERROR" ) == 0 ) return IRC_ERROR( client, Req );
338 else if( strcasecmp( Req->command, "LUSERS" ) == 0 ) return IRC_LUSERS( client, Req );
339 else if( strcasecmp( Req->command, "LINKS" ) == 0 ) return IRC_LINKS( client, Req );
340 else if( strcasecmp( Req->command, "JOIN" ) == 0 ) return IRC_JOIN( client, Req );
341 else if( strcasecmp( Req->command, "PART" ) == 0 ) return IRC_PART( client, Req );
342 else if( strcasecmp( Req->command, "VERSION" ) == 0 ) return IRC_VERSION( client, Req );
343 else if( strcasecmp( Req->command, "KILL" ) == 0 ) return IRC_KILL( client, Req );
344 else if( strcasecmp( Req->command, "AWAY" ) == 0 ) return IRC_AWAY( client, Req );
345 else if( strcasecmp( Req->command, "TOPIC" ) == 0 ) return IRC_TOPIC( client, Req );
346 else if( strcasecmp( Req->command, "WHO" ) == 0 ) return IRC_WHO( client, Req );
347 else if( strcasecmp( Req->command, "LIST" ) == 0 ) return IRC_LIST( client, Req );
348 else if( strcasecmp( Req->command, "INVITE" ) == 0 ) return IRC_INVITE( client, Req );
349 else if( strcasecmp( Req->command, "KICK" ) == 0 ) return IRC_KICK( client, Req );
350 else if( strcasecmp( Req->command, "CONNECT" ) == 0 ) return IRC_CONNECT( client, Req );
351 else if( strcasecmp( Req->command, "ADMIN" ) == 0 ) return IRC_ADMIN( client, Req );
352 #ifdef IRCPLUS
353 else if( strcasecmp( Req->command, "CHANINFO" ) == 0 ) return IRC_CHANINFO( client, Req );
354 #endif
356 /* Unbekannter Befehl */
357 if( Client_Type( client ) != CLIENT_SERVER ) IRC_WriteStrClient( client, ERR_UNKNOWNCOMMAND_MSG, Client_ID( client ), Req->command );
358 Log( LOG_DEBUG, "Connection %d: Unknown command \"%s\", %d %s,%s prefix.", Client_Conn( client ), Req->command, Req->argc, Req->argc == 1 ? "parameter" : "parameters", Req->prefix ? "" : " no" );
360 return TRUE;
361 } /* Handle_Request */
364 /* -eof- */