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