]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conn.c
- kleinere Aenderungen an den Log-Meldungen,
[ngircd-alex.git] / src / ngircd / conn.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001 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 comBase beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: conn.c,v 1.7 2001/12/21 22:24:25 alex Exp $
13  *
14  * connect.h: Verwaltung aller Netz-Verbindungen ("connections")
15  *
16  * $Log: conn.c,v $
17  * Revision 1.7  2001/12/21 22:24:25  alex
18  * - kleinere Aenderungen an den Log-Meldungen,
19  * - Parse_Request() wird aufgerufen.
20  *
21  * Revision 1.6  2001/12/15 00:11:55  alex
22  * - Lese- und Schreib-Puffer implementiert.
23  * - einige neue (Unter-)Funktionen eingefuehrt.
24  * - diverse weitere kleinere Aenderungen.
25  *
26  * Revision 1.5  2001/12/14 08:16:47  alex
27  * - Begonnen, Client-spezifische Lesepuffer zu implementieren.
28  * - Umstellung auf Datentyp "CONN_ID".
29  *
30  * Revision 1.4  2001/12/13 02:04:16  alex
31  * - boesen "Speicherschiesser" in Log() gefixt.
32  *
33  * Revision 1.3  2001/12/13 01:33:09  alex
34  * - Conn_Handler() unterstuetzt nun einen Timeout.
35  * - fuer Verbindungen werden keine FILE-Handles mehr benutzt.
36  * - kleinere "Code Cleanups" ;-)
37  *
38  * Revision 1.2  2001/12/12 23:32:02  alex
39  * - diverse Erweiterungen und Verbesserungen (u.a. sind nun mehrere
40  *   Verbindungen und Listen-Sockets moeglich).
41  *
42  * Revision 1.1  2001/12/12 17:18:38  alex
43  * - Modul zur Verwaltung aller Netzwerk-Verbindungen begonnen.
44  */
45
46
47 #include <portab.h>
48 #include "global.h"
49
50 #include <imp.h>
51 #include <assert.h>
52 #include <stdio.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <string.h>
57 #include <sys/socket.h> 
58 #include <sys/time.h>
59 #include <sys/types.h> 
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
62
63 #ifdef HAVE_STDINT_H
64 #include <stdint.h>                     /* u.a. fuer Mac OS X */
65 #endif
66
67 #include "ngircd.h"
68 #include "log.h"
69 #include "parse.h"
70 #include "tool.h"
71
72 #include <exp.h>
73 #include "conn.h"
74
75
76 #define MAX_CONNECTIONS 100             /* max. Anzahl von Verbindungen an diesem Server */
77
78 #define MAX_CMDLEN 512                  /* max. Laenge eines Befehls, vgl. RFC 2812, 3.2 */
79
80 #define READBUFFER_LEN 2 * MAX_CMDLEN   /* Laenge des Lesepuffers je Verbindung (Bytes) */
81 #define WRITEBUFFER_LEN 4096            /* Laenge des Schreibpuffers je Verbindung (Bytes) */
82
83
84 typedef struct _Connection
85 {
86         INT sock;                       /* Socket Handle */
87         struct sockaddr_in addr;        /* Adresse des Client */
88         CHAR rbuf[READBUFFER_LEN + 1];  /* Lesepuffer */
89         INT rdatalen;                   /* Laenge der Daten im Lesepuffer */
90         CHAR wbuf[WRITEBUFFER_LEN + 1]; /* Schreibpuffer */
91         INT wdatalen;                   /* Laenge der Daten im Schreibpuffer */
92 } CONNECTION;
93
94
95 LOCAL VOID Handle_Read( INT sock );
96 LOCAL BOOLEAN Handle_Write( CONN_ID Idx );
97 LOCAL VOID New_Connection( INT Sock );
98 LOCAL CONN_ID Socket2Index( INT Sock );
99 LOCAL VOID Close_Connection( CONN_ID Idx, CHAR *Msg );
100 LOCAL VOID Read_Request( CONN_ID Idx );
101 LOCAL BOOLEAN Try_Write( CONN_ID Idx );
102
103
104 LOCAL fd_set My_Listener;
105 LOCAL fd_set My_Sockets;
106
107 LOCAL INT My_Max_Fd;
108
109 LOCAL CONNECTION My_Connections[MAX_CONNECTIONS];
110
111
112 GLOBAL VOID Conn_Init( VOID )
113 {
114         CONN_ID i;
115
116         /* zu Beginn haben wir keine Verbindungen */
117         FD_ZERO( &My_Listener );
118         FD_ZERO( &My_Sockets );
119         
120         My_Max_Fd = 0;
121         
122         /* Connection-Struktur initialisieren */
123         for( i = 0; i < MAX_CONNECTIONS; i++ ) My_Connections[i].sock = NONE;
124 } /* Conn_Init */
125
126
127 GLOBAL VOID Conn_Exit( VOID )
128 {
129         CONN_ID idx;
130         INT i;
131         
132         /* Sockets schliessen */
133         for( i = 0; i < My_Max_Fd + 1; i++ )
134         {
135                 if( FD_ISSET( i, &My_Sockets ))
136                 {
137                         for( idx = 0; idx < MAX_CONNECTIONS; idx++ )
138                         {
139                                 if( My_Connections[idx].sock == i ) break;
140                         }
141                         if( idx < MAX_CONNECTIONS ) Close_Connection( idx, "Server going down ..." );
142                         else if( FD_ISSET( i, &My_Listener ))
143                         {
144                                 close( i );
145                                 Log( LOG_INFO, "Listening socket %d closed.", i );
146                         }
147                         else
148                         {
149                                 close( i );
150                                 Log( LOG_WARNING, "Unknown connection %d closed.", i );
151                         }
152                 }
153         }
154 } /* Conn_Exit */
155
156
157 GLOBAL BOOLEAN Conn_New_Listener( CONST INT Port )
158 {
159         /* Neuen Listen-Socket erzeugen: der Server wartet dann
160          * auf dem angegebenen Port auf Verbindungen. */
161
162         struct sockaddr_in addr;
163         INT sock, on = 1;
164
165         /* Server-"Listen"-Socket initialisieren */
166         memset( &addr, 0, sizeof( addr ));
167         addr.sin_family = AF_INET;
168         addr.sin_port = htons( Port );
169         addr.sin_addr.s_addr = htonl( INADDR_ANY );
170
171         /* Socket erzeugen */
172         sock = socket( PF_INET, SOCK_STREAM, 0);
173         if( sock < 0 )
174         {
175                 Log( LOG_ALERT, "Can't create socket: %s", strerror( errno ));
176                 return FALSE;
177         }
178
179         /* Socket-Optionen setzen */
180         if( fcntl( sock, F_SETFL, O_NONBLOCK ) != 0 )
181         {
182                 Log( LOG_ALERT, "Can't enable non-blocking mode: %s", strerror( errno ));
183                 close( sock );
184                 return FALSE;
185         }
186         if( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t)sizeof( on )) != 0)
187         {
188                 Log( LOG_CRIT, "Can't set socket options: %s", strerror( errno ));
189                 /* dieser Fehler kann ignoriert werden. */
190         }
191
192         /* an Port binden */
193         if( bind( sock, (struct sockaddr *)&addr, (socklen_t)sizeof( addr )) != 0 )
194         {
195                 Log( LOG_ALERT, "Can't bind socket: %s", strerror( errno ));
196                 close( sock );
197                 return FALSE;
198         }
199
200         /* in "listen mode" gehen :-) */
201         if( listen( sock, 10 ) != 0 )
202         {
203                 Log( LOG_ALERT, "Can't listen on soecket: %s", strerror( errno ));
204                 close( sock );
205                 return FALSE;
206         }
207
208         /* Neuen Listener in Strukturen einfuegen */
209         FD_SET( sock, &My_Listener );
210         FD_SET( sock, &My_Sockets );
211         
212         if( sock > My_Max_Fd ) My_Max_Fd = sock;
213
214         Log( LOG_INFO, "Now listening on port %d, socket %d.", Port, sock );
215
216         return TRUE;
217 } /* Conn_New_Listener */
218
219
220 GLOBAL VOID Conn_Handler( INT Timeout )
221 {
222         fd_set read_sockets, write_sockets;
223         struct timeval tv;
224         INT i;
225
226         /* Timeout initialisieren */
227         tv.tv_sec = Timeout;
228         tv.tv_usec = 0;
229         
230         /* noch volle Schreib-Puffer suchen */
231         FD_ZERO( &write_sockets );
232         for( i = 0; i < MAX_CONNECTIONS; i++ )
233         {
234                 if(( My_Connections[i].sock >= 0 ) && ( My_Connections[i].wdatalen > 0 ))
235                 {
236                         /* Socket der Verbindung in Set aufnehmen */
237                         FD_SET( My_Connections[i].sock, &write_sockets );
238                 }
239         }
240         
241         read_sockets = My_Sockets;
242         if( select( My_Max_Fd + 1, &read_sockets, &write_sockets, NULL, &tv ) == -1 )
243         {
244                 if( errno != EINTR ) Log( LOG_ALERT, "select(): %s", strerror( errno ));
245                 return;
246         }
247         
248         /* Koennen Daten geschrieben werden? */
249         for( i = 0; i < My_Max_Fd + 1; i++ )
250         {
251                 if( FD_ISSET( i, &write_sockets )) Handle_Write( Socket2Index( i ));
252         }
253         
254         /* Daten zum Lesen vorhanden? */
255         for( i = 0; i < My_Max_Fd + 1; i++ )
256         {
257                 if( FD_ISSET( i, &read_sockets )) Handle_Read( i );
258         }
259 } /* Conn_Handler */
260
261
262 GLOBAL BOOLEAN Conn_WriteStr( CONN_ID Idx, CHAR *Data )
263 {
264         /* String in Socket schreiben. CR+LF wird von dieser Funktion
265          * automatisch angehaengt. */
266         
267         CHAR buffer[MAX_CMDLEN];
268         
269         if( strlen( Data ) > MAX_CMDLEN - 2 )
270         {
271                 Log( LOG_ALERT, "String too long to send (connection %d)!", Idx );
272                 Close_Connection( Idx, "Server error: String too long to send!" );
273                 return FALSE;
274         }
275         
276         sprintf( buffer, "%s\r\n", Data );
277         return Conn_Write( Idx, buffer, strlen( buffer ));
278 } /* Conn_WriteStr */
279
280
281 GLOBAL BOOLEAN Conn_Write( CONN_ID Idx, CHAR *Data, INT Len )
282 {
283         /* Daten in Socket schreiben. Bei "fatalen" Fehlern wird
284          * der Client disconnectiert und FALSE geliefert. */
285         
286         assert( Idx >= 0 );
287         assert( My_Connections[Idx].sock >= 0 );
288         assert( Data != NULL );
289         assert( Len > 0 );
290
291         /* pruefen, ob Daten im Schreibpuffer sind. Wenn ja, zunaechst
292          * pruefen, ob diese gesendet werden koennen */
293         if( My_Connections[Idx].wdatalen > 0 )
294         {
295                 if( ! Try_Write( Idx )) return FALSE;
296         }
297         
298         /* pruefen, ob im Schreibpuffer genuegend Platz ist */
299         if( WRITEBUFFER_LEN - My_Connections[Idx].wdatalen - Len <= 0 )
300         {
301                 /* der Puffer ist dummerweise voll ... */
302                 Log( LOG_NOTICE, "Write buffer overflow (connection %d)!", Idx );
303                 Close_Connection( Idx, NULL );
304                 return FALSE;
305         }
306
307         /* Daten in Puffer kopieren */
308         memcpy( My_Connections[Idx].wbuf + My_Connections[Idx].wdatalen, Data, Len );
309         My_Connections[Idx].wdatalen += Len;
310
311         /* pruefen, on Daten vorhanden sind und geschrieben werden koennen */
312         if( My_Connections[Idx].wdatalen > 0 )
313         {
314                 if( ! Try_Write( Idx )) return FALSE;
315         }
316         
317         return TRUE;
318 } /* Conn_Write */
319
320
321 LOCAL BOOLEAN Try_Write( CONN_ID Idx )
322 {
323         /* Versuchen, Daten aus dem Schreib-Puffer in den
324          * Socket zu schreiben. */
325
326         fd_set write_socket;
327         
328         assert( Idx >= 0 );
329         assert( My_Connections[Idx].sock >= 0 );
330         assert( My_Connections[Idx].wdatalen > 0 );
331
332         FD_ZERO( &write_socket );
333         FD_SET( My_Connections[Idx].sock, &write_socket );
334         if( select( My_Connections[Idx].sock + 1, NULL, &write_socket, NULL, 0 ) == -1 )
335         {
336                 /* Fehler! */
337                 if( errno != EINTR )
338                 {
339                         Log( LOG_ALERT, "select(): %s", strerror( errno ));
340                         Close_Connection( Idx, NULL );
341                         return FALSE;
342                 }
343         }
344
345         if( FD_ISSET( My_Connections[Idx].sock, &write_socket )) return Handle_Write( Idx );
346         else return TRUE;
347 } /* Try_Write */
348
349
350 LOCAL VOID Handle_Read( INT Sock )
351 {
352         /* Aktivitaet auf einem Socket verarbeiten */
353
354         CONN_ID idx;
355         
356         assert( Sock >= 0 );
357
358         if( FD_ISSET( Sock, &My_Listener ))
359         {
360                 /* es ist einer unserer Listener-Sockets: es soll
361                  * also eine neue Verbindung aufgebaut werden. */
362
363                 New_Connection( Sock );
364         }
365         else
366         {
367                 /* Ein Client Socket: entweder ein User oder Server */
368                 
369                 idx = Socket2Index( Sock );
370                 Read_Request( idx );
371         }
372 } /* Handle_Read */
373
374
375 LOCAL BOOLEAN Handle_Write( CONN_ID Idx )
376 {
377         /* Daten aus Schreibpuffer versenden */
378         
379         INT len;
380
381         assert( Idx >= 0 );
382         assert( My_Connections[Idx].sock >= 0 );
383         assert( My_Connections[Idx].wdatalen > 0 );
384                 
385         /* Daten schreiben */
386         len = send( My_Connections[Idx].sock, My_Connections[Idx].wbuf, My_Connections[Idx].wdatalen, 0 );
387         if( len < 0 )
388         {
389                 /* Oops, ein Fehler! */
390                 Log( LOG_ALERT, "Write error (buffer) on connection %d: %s", Idx, strerror( errno ));
391                 Close_Connection( Idx, NULL );
392                 return FALSE;
393         }
394         
395         /* Puffer anpassen */
396         My_Connections[Idx].wdatalen -= len;
397         memmove( My_Connections[Idx].wbuf, My_Connections[Idx].wbuf + len, My_Connections[Idx].wdatalen );
398         
399         return TRUE;
400 } /* Handle_Write */
401
402
403 LOCAL VOID New_Connection( INT Sock )
404 {
405         /* Neue Client-Verbindung von Listen-Socket annehmen */
406
407         struct sockaddr_in new_addr;
408         INT new_sock, new_sock_len;
409         CONN_ID idx;
410         
411         assert( Sock >= 0 );
412
413         new_sock_len = sizeof( new_addr );
414         new_sock = accept( Sock, (struct sockaddr *)&new_addr, (socklen_t *)&new_sock_len );
415         if( new_sock < 0 )
416         {
417                 Log( LOG_CRIT, "Can't accept connection: %s", strerror( errno ));
418                 return;
419         }
420                 
421         /* Freie Connection-Struktur suschen */
422         for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock < 0 ) break;
423         if( idx >= MAX_CONNECTIONS )
424         {
425                 Log( LOG_ALERT, "Can't accept connection: limit reached (%d)!", MAX_CONNECTIONS );
426                 close( new_sock );
427                 return;
428         }
429
430         /* Verbindung registrieren */
431         My_Connections[idx].sock = new_sock;
432         My_Connections[idx].addr = new_addr;
433         My_Connections[idx].rdatalen = 0;
434         My_Connections[idx].wdatalen = 0;
435
436         /* Neuen Socket registrieren */
437         FD_SET( new_sock, &My_Sockets );
438
439         if( new_sock > My_Max_Fd ) My_Max_Fd = new_sock;
440
441         Log( LOG_NOTICE, "Accepted connection %d from %s:%d on socket %d.", idx, inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port), Sock );
442 } /* New_Connection */
443
444
445 LOCAL CONN_ID Socket2Index( INT Sock )
446 {
447         /* zum Socket passende Connection suchen */
448
449         CONN_ID idx;
450         
451         assert( Sock >= 0 );
452         
453         for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == Sock ) break;
454         
455         assert( idx < MAX_CONNECTIONS );
456         return idx;
457 } /* Socket2Index */
458
459
460 LOCAL VOID Close_Connection( CONN_ID Idx, CHAR *Msg )
461 {
462         /* Verbindung schlie§en */
463
464         CHAR bye_str[MAX_CMDLEN];
465
466         assert( Idx >= 0 );
467         assert( My_Connections[Idx].sock >= 0 );
468         
469         if( Msg )
470         {
471                 sprintf( bye_str, "ERROR :%s", Msg );
472                 Conn_WriteStr( Idx, bye_str );
473         }
474         
475         if( close( My_Connections[Idx].sock ) != 0 )
476         {
477                 Log( LOG_ERR, "Error closing connection %d with %s:%d - %s", Idx, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
478         }
479         else
480         {
481                 Log( LOG_NOTICE, "Connection %d with %s:%d closed.", Idx, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port ));
482         }
483
484         FD_CLR( My_Connections[Idx].sock, &My_Sockets );
485         My_Connections[Idx].sock = NONE;
486 } /* Close_Connection */
487
488
489 LOCAL VOID Read_Request( CONN_ID Idx )
490 {
491         /* Daten von Socket einlesen und entsprechend behandeln.
492          * Tritt ein Fehler auf, so wird der Socket geschlossen. */
493
494         INT len;
495         CHAR *ptr;
496
497         assert( Idx >= 0 );
498         assert( My_Connections[Idx].sock >= 0 );
499         
500         len = recv( My_Connections[Idx].sock, My_Connections[Idx].rbuf + My_Connections[Idx].rdatalen, READBUFFER_LEN - My_Connections[Idx].rdatalen, 0 );
501         My_Connections[Idx].rbuf[READBUFFER_LEN] = '\0';
502
503         if( len == 0 )
504         {
505                 /* Socket wurde geschlossen */
506                 Log( LOG_INFO, "%s:%d is closing the connection ...", inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port));
507                 Close_Connection( Idx, NULL );
508                 return;
509         }
510
511         if( len < 0 )
512         {
513                 /* Fehler beim Lesen */
514                 Log( LOG_ALERT, "Read error on connection %d!", Idx );
515                 Close_Connection( Idx, "Read error!" );
516                 return;
517         }
518
519         My_Connections[Idx].rdatalen += len;
520         assert( My_Connections[Idx].rdatalen <= READBUFFER_LEN );
521         My_Connections[Idx].rbuf[My_Connections[Idx].rdatalen] = '\0';
522
523         if( My_Connections[Idx].rdatalen > MAX_CMDLEN )
524         {
525                 /* Eine Anfrage darf(!) nicht laenger als 512 Zeichen
526                  * (incl. CR+LF!) werden; vgl. RFC 2812. Wenn soetwas
527                  * empfangen wird, wird der Client disconnectiert. */
528                 Log( LOG_ALERT, "Request too long (connection %d)!", Idx );
529                 Close_Connection( Idx, "Request too long!" );
530                 return;
531         }
532         
533         /* Eine komplette Anfrage muss mit CR+LF enden, vgl.
534          * RFC 2812. Haben wir eine? */
535         ptr = strstr( My_Connections[Idx].rbuf, "\r\n" );
536         if( ptr )
537         {
538                 /* Ende der Anfrage (CR+LF) wurde gefunden */
539                 *ptr = '\0';
540                 len = ( ptr - My_Connections[Idx].rbuf ) + 2;
541                 if( len > 2 )
542                 {
543                         /* Es wurde ein Request gelesen */
544                         if( ! Parse_Request( Idx, My_Connections[Idx].rbuf )) return;
545                 }
546                 else Log( LOG_DEBUG, "Got null-request (connection %d).", Idx );
547
548                 /* Puffer anpassen */
549                 My_Connections[Idx].rdatalen -= len;
550                 memmove( My_Connections[Idx].rbuf, My_Connections[Idx].rbuf + len, My_Connections[Idx].rdatalen );
551         }
552 } /* Read_Request */
553
554
555 /* -eof- */