]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conn.c
- boesen "Speicherschiesser" in Log() gefixt.
[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.4 2001/12/13 02:04:16 alex Exp $
13  *
14  * connect.h: Verwaltung aller Netz-Verbindungen ("connections")
15  *
16  * $Log: conn.c,v $
17  * Revision 1.4  2001/12/13 02:04:16  alex
18  * - boesen "Speicherschiesser" in Log() gefixt.
19  *
20  * Revision 1.3  2001/12/13 01:33:09  alex
21  * - Conn_Handler() unterstuetzt nun einen Timeout.
22  * - fuer Verbindungen werden keine FILE-Handles mehr benutzt.
23  * - kleinere "Code Cleanups" ;-)
24  *
25  * Revision 1.2  2001/12/12 23:32:02  alex
26  * - diverse Erweiterungen und Verbesserungen (u.a. sind nun mehrere
27  *   Verbindungen und Listen-Sockets moeglich).
28  *
29  * Revision 1.1  2001/12/12 17:18:38  alex
30  * - Modul zur Verwaltung aller Netzwerk-Verbindungen begonnen.
31  */
32
33
34 #include <portab.h>
35 #include "global.h"
36
37 #include <imp.h>
38 #include <assert.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <string.h>
44 #include <sys/socket.h> 
45 #include <sys/time.h>
46 #include <sys/types.h> 
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49
50 #ifdef HAVE_STDINT_H
51 #include <stdint.h>                     /* u.a. fuer Mac OS X */
52 #endif
53
54 #include "ngircd.h"
55 #include "log.h"
56 #include "tool.h"
57
58 #include <exp.h>
59 #include "conn.h"
60
61
62 #define MAX_CONNECTIONS 100
63
64
65 typedef struct _Connection
66 {
67         INT sock;                       /* Socket Handle */
68         struct sockaddr_in addr;        /* Adresse des Client */
69 } CONNECTION;
70
71
72 LOCAL VOID Handle_Socket( INT sock );
73
74 LOCAL VOID New_Connection( INT Sock );
75
76 LOCAL INT Socket2Index( INT Sock );
77
78 LOCAL VOID Close_Connection( INT Idx );
79 LOCAL VOID Read_Request( INT Idx );
80
81 LOCAL BOOLEAN Send( INT Idx, CHAR *Data );
82
83
84 LOCAL fd_set My_Listener;
85 LOCAL fd_set My_Sockets;
86
87 LOCAL INT My_Max_Fd;
88
89 LOCAL CONNECTION My_Connections[MAX_CONNECTIONS];
90
91
92 GLOBAL VOID Conn_Init( VOID )
93 {
94         INT i;
95
96         /* zu Beginn haben wir keine Verbindungen */
97         FD_ZERO( &My_Listener );
98         FD_ZERO( &My_Sockets );
99         
100         My_Max_Fd = 0;
101         
102         /* Connection-Struktur initialisieren */
103         for( i = 0; i < MAX_CONNECTIONS; i++ )
104         {
105                 My_Connections[i].sock = -1;
106         }
107 } /* Conn_Init */
108
109
110 GLOBAL VOID Conn_Exit( VOID )
111 {
112         INT idx, i;
113         
114         /* Sockets schliessen */
115         for( i = 0; i < My_Max_Fd + 1; i++ )
116         {
117                 if( FD_ISSET( i, &My_Sockets ))
118                 {
119                         for( idx = 0; idx < MAX_CONNECTIONS; idx++ )
120                         {
121                                 if( My_Connections[idx].sock == i ) break;
122                         }
123                         if( idx < MAX_CONNECTIONS ) Close_Connection( idx );
124                         else if( FD_ISSET( i, &My_Listener ))
125                         {
126                                 close( i );
127                                 Log( LOG_INFO, "Closed listening socket %d.", i );
128                         }
129                         else
130                         {
131                                 close( i );
132                                 Log( LOG_WARNING, "Unknown connection %d closed.", i );
133                         }
134                 }
135         }
136 } /* Conn_Exit */
137
138
139 GLOBAL BOOLEAN Conn_New_Listener( CONST INT Port )
140 {
141         /* Neuen Listen-Socket erzeugen: der Server wartet dann
142          * auf dem angegebenen Port auf Verbindungen. */
143
144         struct sockaddr_in addr;
145         INT sock, on = 1;
146
147         /* Server-"Listen"-Socket initialisieren */
148         memset( &addr, 0, sizeof( addr ));
149         addr.sin_family = AF_INET;
150         addr.sin_port = htons( Port );
151         addr.sin_addr.s_addr = htonl( INADDR_ANY );
152
153         /* Socket erzeugen */
154         sock = socket( PF_INET, SOCK_STREAM, 0);
155         if( sock < 0 )
156         {
157                 Log( LOG_ALERT, "Can't create socket: %s", strerror( errno ));
158                 return FALSE;
159         }
160
161         /* Socket-Optionen setzen */
162         if( fcntl( sock, F_SETFL, O_NONBLOCK ) != 0 )
163         {
164                 Log( LOG_ALERT, "Can't enable non-blocking mode: %s", strerror( errno ));
165                 close( sock );
166                 return FALSE;
167         }
168         if( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t)sizeof( on )) != 0)
169         {
170                 Log( LOG_CRIT, "Can't set socket options: %s", strerror( errno ));
171                 /* dieser Fehler kann ignoriert werden. */
172         }
173
174         /* an Port binden */
175         if( bind( sock, (struct sockaddr *)&addr, (socklen_t)sizeof( addr )) != 0 )
176         {
177                 Log( LOG_ALERT, "Can't bind socket: %s", strerror( errno ));
178                 close( sock );
179                 return FALSE;
180         }
181
182         /* in "listen mode" gehen :-) */
183         if( listen( sock, 10 ) != 0 )
184         {
185                 Log( LOG_ALERT, "Can't listen on soecket: %s", strerror( errno ));
186                 close( sock );
187                 return FALSE;
188         }
189
190         /* Neuen Listener in Strukturen einfuegen */
191         FD_SET( sock, &My_Listener );
192         FD_SET( sock, &My_Sockets );
193         
194         if( sock > My_Max_Fd ) My_Max_Fd = sock;
195
196         Log( LOG_INFO, "Now listening on port %d, socked %d.", Port, sock );
197
198         return TRUE;
199 } /* Conn_New_Listener */
200
201
202 GLOBAL VOID Conn_Handler( INT Timeout )
203 {
204         fd_set read_sockets;
205         struct timeval tv;
206         INT i;
207
208         /* Timeout initialisieren */
209         tv.tv_sec = Timeout;
210         tv.tv_usec = 0;
211         
212         read_sockets = My_Sockets;
213         if( select( My_Max_Fd + 1, &read_sockets, NULL, NULL, &tv ) == -1 )
214         {
215                 if( errno != EINTR ) Log( LOG_ALERT, "select(): %s", strerror( errno ));
216                 return;
217         }
218         
219         for( i = 0; i < My_Max_Fd + 1; i++ )
220         {
221                 if( FD_ISSET( i, &read_sockets )) Handle_Socket( i );
222         }
223 } /* Conn_Handler */
224
225
226 LOCAL VOID Handle_Socket( INT Sock )
227 {
228         /* Aktivitaet auf einem Socket verarbeiten */
229
230         INT idx;
231
232         if( FD_ISSET( Sock, &My_Listener ))
233         {
234                 /* es ist einer unserer Listener-Sockets: es soll
235                  * also eine neue Verbindung aufgebaut werden. */
236
237                 New_Connection( Sock );
238         }
239         else
240         {
241                 /* Ein Client Socket: entweder ein User oder Server */
242                 
243                 idx = Socket2Index( Sock );
244                 Read_Request( idx );
245         }
246 } /* Handle_Socket */
247
248
249 LOCAL VOID New_Connection( INT Sock )
250 {
251         struct sockaddr_in new_addr;
252         INT new_sock, new_sock_len, idx;
253
254         new_sock_len = sizeof( new_addr );
255         new_sock = accept( Sock, (struct sockaddr *)&new_addr, (socklen_t *)&new_sock_len );
256         if( new_sock < 0 )
257         {
258                 Log( LOG_CRIT, "Can't accept connection: %s", strerror( errno ));
259                 return;
260         }
261                 
262         /* Freie Connection-Struktur suschen */
263         for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock < 0 ) break;
264         if( idx >= MAX_CONNECTIONS )
265         {
266                 Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", MAX_CONNECTIONS );
267                 close( new_sock );
268                 return;
269         }
270
271         /* Verbindung registrieren */
272         My_Connections[idx].sock = new_sock;
273         My_Connections[idx].addr = new_addr;
274
275         /* Neuen Socket registrieren */
276         FD_SET( new_sock, &My_Sockets );
277
278         if( new_sock > My_Max_Fd ) My_Max_Fd = new_sock;
279
280         Send( idx, "hello world!\n" );
281
282         Log( LOG_INFO, "Accepted connection from %s:%d.", inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port));
283 } /* New_Connection */
284
285
286 LOCAL INT Socket2Index( INT Sock )
287 {
288         INT idx;
289         
290         for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == Sock ) break;
291         assert( idx < MAX_CONNECTIONS );
292         
293         return idx;
294 } /* Socket2Index */
295
296
297 LOCAL VOID Close_Connection( INT Idx )
298 {
299         /* Verbindung schlie§en */
300
301         assert( My_Connections[Idx].sock >= 0 );
302         
303         if( close( My_Connections[Idx].sock ) != 0 )
304         {
305                 Log( LOG_ERR, "Error closing connection with %s:%d - %s", inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
306         }
307         else
308         {
309                 Log( LOG_INFO, "Closed connection with %s:%d.", inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port ));
310         }
311
312         FD_CLR( My_Connections[Idx].sock, &My_Sockets );
313         My_Connections[Idx].sock = -1;
314 } /* Close_Connection */
315
316
317 LOCAL VOID Read_Request( INT Idx )
318 {
319         /* Daten von Socket einlesen und entsprechend behandeln.
320          * Tritt ein Fehler auf, so wird der Socket geschlossen. */
321
322         #define SIZE 256
323         
324         CHAR buffer[SIZE];
325         INT len;
326         
327         len = recv( My_Connections[Idx].sock, buffer, SIZE, 0 );
328
329         if( len == 0 )
330         {
331                 /* Socket wurde geschlossen */
332                 Close_Connection( Idx );
333                 return;
334         }
335
336         if( len < 0 )
337         {
338                 /* Fehler beim Lesen */
339                 Log( LOG_ALERT, "Read error on socket %d!", My_Connections[Idx].sock );
340                 Close_Connection( Idx );
341                 return;
342         }
343         
344         ngt_Trim_Str( buffer );
345         printf( " in: '%s'\n", buffer );
346 } /* Read_Data */
347
348
349 LOCAL BOOLEAN Send( INT Idx, CHAR *Data )
350 {
351         /* Daten in Socket schreiben, ggf. in mehreren Stuecken. Tritt
352          * ein Fehler auf, so wird die Verbindung beendet und FALSE
353          * als Rueckgabewert geliefert. */
354         
355         INT n, sent, len;
356                 
357         sent = 0;
358         len = strlen( Data );
359         
360         while( sent < len )
361         {
362                 n = send( My_Connections[Idx].sock, Data + sent, len - sent, 0 );
363                 if( n <= 0 )
364                 {
365                         /* Oops, ein Fehler! */
366                         Log( LOG_ALERT, "Write error on socket %d!", My_Connections[Idx].sock );
367                         Close_Connection( Idx );
368                         return FALSE;
369                 }
370                 sent += n;
371         }
372         return TRUE;
373 } /* Send */
374
375
376 /* -eof- */