Updated init script.
[dtbartle/bnbt.git] / server.cpp
1 /***\r
2 *\r
3 * BNBT Beta 8.0 - A C++ BitTorrent Tracker\r
4 * Copyright (C) 2003-2004 Trevor Hogan\r
5 *\r
6 * CBTT variations (C) 2003-2005 Harold Feit\r
7 *\r
8 * This library is free software; you can redistribute it and/or\r
9 * modify it under the terms of the GNU Lesser General Public\r
10 * License as published by the Free Software Foundation; either\r
11 * version 2.1 of the License, or (at your option) any later version.\r
12 *\r
13 * This library is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
16 * Lesser General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU Lesser General Public\r
19 * License along with this library; if not, write to the Free Software\r
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
21 *\r
22 ***/\r
23 \r
24 #include "bnbt.h"\r
25 #include "atom.h"\r
26 #include "client.h"\r
27 #include "config.h"\r
28 #include "server.h"\r
29 #include "tracker.h"\r
30 #include "util.h"\r
31 \r
32 #ifdef WIN32\r
33  #include "util_ntservice.h"\r
34 #endif\r
35 \r
36 CServer :: CServer( )\r
37 {\r
38         m_bKill = false;\r
39 \r
40         m_iSocketTimeOut = CFG_GetInt( "socket_timeout", 15 );\r
41         m_strBind = CFG_GetString( "bind", string( ) );\r
42         m_iCompression = CFG_GetInt( "bnbt_compression_level", 6 );\r
43 \r
44         // clamp\r
45 \r
46         if( m_iCompression > 9 )\r
47                 m_iCompression = 9;\r
48 \r
49 \r
50         // Binding socket to IP and port\r
51 \r
52         struct sockaddr_in sin;\r
53 \r
54         memset( &sin, 0, sizeof( sin ) );\r
55 \r
56         sin.sin_family = AF_INET;\r
57 \r
58         if( !m_strBind.empty( ) )\r
59         {\r
60                 // bind to m_strBind\r
61 \r
62                 if( gbDebug )\r
63                         UTIL_LogPrint( "server - binding to %s\n", m_strBind.c_str( ) );\r
64 \r
65                 sin.sin_addr.s_addr = inet_addr( m_strBind.c_str( ) );\r
66 \r
67                 if( sin.sin_addr.s_addr == INADDR_NONE || sin.sin_addr.s_addr == 0 )\r
68                 {\r
69                         UTIL_LogPrint( "server error - unable to bind to %s\n", m_strBind.c_str( ) );\r
70 \r
71                         Kill( );\r
72                 }\r
73         }\r
74         else\r
75         {\r
76                 // bind to all available addresses\r
77 \r
78                 if( gbDebug )\r
79                         UTIL_LogPrint( "server - binding to all available addresses\n" );\r
80 \r
81                 sin.sin_addr.s_addr = INADDR_ANY;\r
82         }\r
83 \r
84         // Multiple Listen Ports\r
85         // Checking for legacy configuration values (compatibility)\r
86 \r
87         if( ( sin.sin_port = htons( (u_short)CFG_GetInt("port",26213))) == 0)\r
88                 UTIL_LogPrint( "server warning - invalid port %d (\"port\"), ignoring\n",CFG_GetInt("port",26213));\r
89         else if( !AddListener( sin ) )\r
90                 UTIL_LogPrint( "server warning - unable to add listener on port %d (\"port\")\n",CFG_GetInt("port",26213));\r
91         else\r
92                 UTIL_LogPrint( "server - listening on port %d (\"port\")\n",CFG_GetInt("port",26213));\r
93 \r
94         \r
95         // Multiple Listen Ports\r
96         // Checking for new configuration values\r
97 \r
98         int iPort = 1;\r
99 \r
100         string strName = "port" + CAtomInt( iPort ).toString();\r
101         string strPort = CFG_GetString( strName, string ());\r
102 \r
103         while( !strPort.empty() )\r
104         {\r
105                 if( (sin.sin_port = htons( (u_short)atoi( strPort.c_str() ) ) ) == 0 )\r
106                         UTIL_LogPrint( "server warning - invalid port %d (\"%s\"), ignoring\n", atoi( strPort.c_str() ),strName.c_str() );\r
107                 else if(!AddListener( sin ) )\r
108                         UTIL_LogPrint( "server warning - unable to add listener on port %d (\"%s\")\n", atoi( strPort.c_str() ),strName.c_str() );\r
109                 else\r
110                         UTIL_LogPrint( "server - listening on port %d (\"%s\")\n", atoi( strPort.c_str() ),strName.c_str() );\r
111 \r
112                 iPort++;\r
113                 strName = "port" + CAtomInt( iPort ).toString();\r
114                 strPort = CFG_GetString( strName, string ());\r
115         }\r
116 \r
117         // verifying that we are listening on at least 1 port\r
118         if( m_vecListeners.empty() )\r
119         {\r
120                 UTIL_LogPrint("server error - not listening on any ports\n");\r
121                 exit(1);\r
122         }\r
123 \r
124 \r
125         /*sin.sin_port = htons( (u_short)CFG_GetInt( "port", 26213 ) );\r
126 \r
127         if( sin.sin_port < 1 || sin.sin_port > 65535 )\r
128         {\r
129                 UTIL_LogPrint( "server error - invalid port %d\n", CFG_GetInt( "port", 26213 ) );\r
130 \r
131                 Kill( );\r
132         }*/\r
133 \r
134         m_pTracker = new CTracker( );\r
135 \r
136         UTIL_LogPrint( "server - start\n" );\r
137 }\r
138 \r
139 CServer :: ~CServer( )\r
140 {\r
141         unsigned long iStart = GetTime( );\r
142 \r
143         while( 1 )\r
144         {\r
145                 for( vector<CClient *> :: iterator i = m_vecClients.begin( ); i != m_vecClients.end( ); )\r
146                 {\r
147                         if( (*i)->m_iState == CS_WAITING1 || (*i)->m_iState == CS_WAITING2 || (*i)->m_iState == CS_DEAD )\r
148                         {\r
149                                 delete *i;\r
150 \r
151                                 i = m_vecClients.erase( i );\r
152                         }\r
153                         else\r
154                                 i++;\r
155                 }\r
156 \r
157                 if( !m_vecClients.empty( ) )\r
158                 {\r
159                         UTIL_LogPrint( "server - waiting for %d clients to disconnect\n", m_vecClients.size( ) );\r
160 \r
161                         MILLISLEEP( 1000 );\r
162                 }\r
163                 else\r
164                         break;\r
165 \r
166                 if( GetTime( ) - iStart > 60 )\r
167                 {\r
168                         UTIL_LogPrint( "server - waited 60 seconds, exiting anyway\n" );\r
169 \r
170                         break;\r
171                 }\r
172         }\r
173 \r
174         for( vector<SOCKET> :: iterator i = m_vecListeners.begin( ); i != m_vecListeners.end( ); i++ )\r
175                 closesocket( *i );\r
176 \r
177         for( vector<CClient *> :: iterator j = m_vecClients.begin( ); j != m_vecClients.end( ); j++ )\r
178                 delete *j;\r
179 \r
180         m_vecListeners.clear( );\r
181         m_vecClients.clear( );\r
182 \r
183         if( m_pTracker )\r
184                 delete m_pTracker;\r
185 \r
186         m_pTracker = NULL;\r
187 \r
188         UTIL_LogPrint( "server - exit\n" );\r
189 }\r
190 \r
191 void CServer :: Kill( )\r
192 {\r
193         m_bKill = true;\r
194 }\r
195 \r
196 bool CServer :: isDying( )\r
197 {\r
198         return m_bKill;\r
199 }\r
200 \r
201 bool CServer :: Update( bool bBlock )\r
202 {\r
203         if( m_vecClients.size( ) < giMaxConns )\r
204         {\r
205                 for( vector<SOCKET> :: iterator i = m_vecListeners.begin( ); i != m_vecListeners.end( ); i++ )\r
206                 {\r
207                 fd_set fdServer;\r
208 \r
209                 FD_ZERO( &fdServer );\r
210                 FD_SET( *i, &fdServer );\r
211 \r
212                 struct timeval tv;\r
213 \r
214                 if( bBlock )\r
215                 {\r
216                         // block for 100 ms to keep from eating up all cpu time\r
217 \r
218                         tv.tv_sec = 0;\r
219                         tv.tv_usec = 100000;\r
220                 }\r
221                 else\r
222                 {\r
223                         tv.tv_sec = 0;\r
224                         tv.tv_usec = 0;\r
225                 }\r
226 \r
227 #ifdef WIN32\r
228                 if( select( 1, &fdServer, NULL, NULL, &tv ) == SOCKET_ERROR )\r
229 #else\r
230                 if( select( *i + 1, &fdServer, NULL, NULL, &tv ) == SOCKET_ERROR )\r
231 #endif\r
232                 {\r
233                         UTIL_LogPrint( "server warning - select error (error %d)\n", GetLastError( ) );\r
234 \r
235                         FD_ZERO( &fdServer );\r
236 \r
237                         MILLISLEEP( 100 );\r
238                 }\r
239 \r
240                 if( FD_ISSET( *i, &fdServer ) )\r
241                 {\r
242                         struct sockaddr_in adrFrom;\r
243 \r
244                         int iAddrLen = sizeof( adrFrom );\r
245 \r
246                         SOCKET sckClient;\r
247 \r
248 #ifdef WIN32\r
249                         sckClient = accept( *i, (SOCKADDR*)&adrFrom, &iAddrLen );\r
250 \r
251                         if( sckClient == INVALID_SOCKET )\r
252 #else\r
253                         sckClient = accept( *i, (struct sockaddr *)&adrFrom, (socklen_t *)&iAddrLen );\r
254 \r
255                         if( sckClient == INVALID_SOCKET )\r
256 #endif\r
257                                 UTIL_LogPrint( "server warning - accept error (error %d)\n", GetLastError( ) );\r
258                         else\r
259                         {\r
260                                 // reuse the old timeval structure just because\r
261 \r
262                                 tv.tv_sec = m_iSocketTimeOut;\r
263                                 tv.tv_usec = 0;\r
264 \r
265                                 CClient *pClient = new CClient( sckClient, adrFrom, tv, m_iCompression );\r
266 \r
267 #ifdef WIN32\r
268                                 if( _beginthread( ( void (*)(void *) )StartReceiving, 0, (void *)pClient ) == -1 )\r
269                                 {\r
270                                         UTIL_LogPrint( "server warning - unable to spawn receiver thread\n" );\r
271 \r
272                                         pClient->m_iState = CS_DEAD;\r
273                                 }\r
274 #else\r
275                                 pthread_t thread;\r
276 \r
277                                 // set detached state since we don't need to join with any threads\r
278 \r
279                                 pthread_attr_t attr;\r
280                                 pthread_attr_init( &attr );\r
281                                 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );\r
282 \r
283                                 int c = pthread_create( &thread, &attr, ( void * (*)(void *) )StartReceiving, (void *)pClient );\r
284 \r
285                                 if( c != 0 )\r
286                                 {\r
287                                         UTIL_LogPrint( "server warning - unable to spawn receiver thread (error %s)\n", strerror( c ) );\r
288 \r
289                                         pClient->m_iState = CS_DEAD;\r
290                                 }\r
291 #endif\r
292 \r
293                                 m_vecClients.push_back( pClient );\r
294                         }\r
295                 }\r
296                 }\r
297         }\r
298         else\r
299         {\r
300                 // maximum connections reached\r
301 \r
302                 MILLISLEEP( 10 );\r
303         }\r
304 \r
305         // process\r
306 \r
307         for( vector<CClient *> :: iterator i = m_vecClients.begin( ); i != m_vecClients.end( ); )\r
308         {\r
309                 if( (*i)->m_iState == CS_WAITING1 )\r
310                 {\r
311                         (*i)->Process( );\r
312 \r
313                         i++;\r
314                 }\r
315                 else if( (*i)->m_iState == CS_WAITING2 )\r
316                 {\r
317                         (*i)->m_iState = CS_SENDING;\r
318 \r
319 #ifdef WIN32\r
320                         if( _beginthread( ( void (*)(void *) )StartSending, 0, (void *)(*i) ) == -1 )\r
321                         {\r
322                                 UTIL_LogPrint( "server warning - unable to spawn sender thread\n" );\r
323 \r
324                                 (*i)->m_iState = CS_DEAD;\r
325                         }\r
326 #else\r
327                         pthread_t thread;\r
328 \r
329                         // set detached state since we don't need to join with any threads\r
330 \r
331                         pthread_attr_t attr;\r
332                         pthread_attr_init( &attr );\r
333                         pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );\r
334 \r
335                         int c = pthread_create( &thread, &attr, ( void * (*)(void *) )StartSending, (void *)(*i) );\r
336 \r
337                         if( c != 0 )\r
338                         {\r
339                                 UTIL_LogPrint( "server warning - unable to spawn sender thread (error %s)\n", strerror( c ) );\r
340 \r
341                                 (*i)->m_iState = CS_DEAD;\r
342                         }\r
343 #endif\r
344 \r
345                         i++;\r
346                 }\r
347                 else if( (*i)->m_iState == CS_DEAD )\r
348                 {\r
349                         delete *i;\r
350 \r
351                         i = m_vecClients.erase( i );\r
352                 }\r
353                 else\r
354                         i++;\r
355         }\r
356 \r
357         if( m_pTracker )\r
358                 m_pTracker->Update( );\r
359 \r
360         return m_bKill;\r
361 }\r
362 \r
363 CTracker *CServer :: getTracker( )\r
364 {\r
365         return m_pTracker;\r
366 }\r
367 \r
368 bool CServer :: AddListener( struct sockaddr_in sin )\r
369 {\r
370         SOCKET sckListener;\r
371 \r
372         // map protocol name to protocol number\r
373 \r
374         struct protoent *pPE;\r
375         \r
376         pPE = getprotobyname( "tcp" );\r
377 \r
378         if( pPE == 0 )\r
379         {\r
380                 UTIL_LogPrint( "server error - unable to get tcp protocol entry (error %d)\n", GetLastError( ) );\r
381 \r
382                 return false;\r
383         }\r
384 \r
385         // allocate socket\r
386         \r
387         sckListener = socket( PF_INET, SOCK_STREAM, pPE->p_proto );\r
388 \r
389         if( sckListener == INVALID_SOCKET )\r
390         {\r
391                 UTIL_LogPrint( "server error - unable to allocate socket (error %d)\n", GetLastError( ) );\r
392 \r
393                 return false;\r
394         }\r
395         // bind socket\r
396 \r
397         int iOptVal = 1;\r
398         const char *on = "1";\r
399 \r
400 #ifdef WIN32\r
401         // Windows\r
402         // TCP window size \r
403         // Send\r
404         if( setsockopt( sckListener, SOL_SOCKET, SO_SNDBUF, (const char *)&giSO_SNDBUF, sizeof(giSO_SNDBUF) ) == SOCKET_ERROR )\r
405         {\r
406                 UTIL_LogPrint( "server error - setsockopt SO_SNDBUF (%d)\n", GetLastError( ) );\r
407 \r
408                 int rc = closesocket( sckListener );\r
409 \r
410                 if( rc == SOCKET_ERROR )\r
411                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
412                 else\r
413                 {\r
414                         if( gbDebug )\r
415                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
416                 }\r
417 \r
418                 return false;\r
419         }\r
420 \r
421         // Receive\r
422         if( setsockopt( sckListener, SOL_SOCKET, SO_RCVBUF, (const char *)&giSO_RECBUF, sizeof(giSO_RECBUF) ) == SOCKET_ERROR )\r
423         {\r
424                 UTIL_LogPrint( "server error - setsockopt SO_RCVBUF (%d)\n", GetLastError( ) );\r
425 \r
426                 int rc = closesocket( sckListener );\r
427 \r
428                 if( rc == SOCKET_ERROR )\r
429                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
430                 else\r
431                 {\r
432                         if( gbDebug )\r
433                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
434                 }\r
435 \r
436                 return false;\r
437         }\r
438 \r
439         //      Allows the socket to be bound to an address that is already in use.\r
440         if( setsockopt( sckListener, SOL_SOCKET, SO_REUSEADDR, (const char *)&iOptVal, sizeof( int ) ) == SOCKET_ERROR )\r
441         {\r
442                 if(GetLastError() != 0)\r
443                 {\r
444             UTIL_LogPrint( "server warning - setsockopt SO_REUSEADDR (%d)\n", GetLastError( ) );\r
445             UTIL_LogPrint( "Binding to an in-use socket had issues. Will attempt to use the socket anyway.\n");\r
446                 }\r
447         }\r
448 \r
449         // Forcibly Disable the Nagle Algorithm, Speeding up announce requests on Windows systems.\r
450         if( setsockopt( sckListener, SOL_SOCKET, TCP_NODELAY, on, sizeof( on ) ) == SOCKET_ERROR )\r
451                 UTIL_LogPrint( "Server Warning: Disabling of Nagle Failed - %d \n", GetLastError( ) );\r
452 \r
453         if( bind( sckListener, (SOCKADDR*)&sin, sizeof( sin ) ) == SOCKET_ERROR )\r
454         {\r
455                 UTIL_LogPrint( "server error - unable to bind socket (error %d)\n", GetLastError( ) );\r
456                 \r
457                 int rc = closesocket( sckListener );\r
458 \r
459                 if( rc == SOCKET_ERROR )\r
460                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
461                 else\r
462                 {\r
463                         if( gbDebug )\r
464                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
465                 }\r
466 \r
467                 return false;\r
468         }\r
469 #else\r
470         // Everyone Else\r
471         // TCP window size \r
472         // Send\r
473         if( setsockopt( sckListener, SOL_SOCKET, SO_SNDBUF, (const char *)&giSO_SNDBUF, (socklen_t)sizeof(giSO_SNDBUF) ) == SOCKET_ERROR )\r
474         {\r
475                 UTIL_LogPrint( "server error - setsockopt SO_SNDBUF (%d)\n", GetLastError( ) );\r
476 \r
477                 int rc = closesocket( sckListener );\r
478 \r
479                 if( rc == SOCKET_ERROR )\r
480                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
481                 else\r
482                 {\r
483                         if( gbDebug )\r
484                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
485                 }\r
486 \r
487                 return false;\r
488         }\r
489 \r
490         // Receive\r
491         if( setsockopt( sckListener, SOL_SOCKET, SO_RCVBUF, (const char *)&giSO_RECBUF, (socklen_t)sizeof(giSO_RECBUF) ) == SOCKET_ERROR )\r
492         {\r
493                 UTIL_LogPrint( "server error - setsockopt SO_RCVBUF (%d)\n", GetLastError( ) );\r
494 \r
495                 int rc = closesocket( sckListener );\r
496 \r
497                 if( rc == SOCKET_ERROR )\r
498                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
499                 else\r
500                 {\r
501                         if( gbDebug )\r
502                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
503                 }\r
504 \r
505                 return false;\r
506         }\r
507 \r
508         //      Allows the socket to be bound to an address that is already in use.\r
509         // SO_REUSEADDR is not supported on Linux and has some issues on *BSD platforms.\r
510         // If setting the SO_REUSEADDR fails, binding will still attempt to happen.\r
511         if( setsockopt( sckListener, SOL_SOCKET, SO_REUSEADDR, (const void *)&iOptVal, sizeof( int ) ) == SOCKET_ERROR );\r
512         {\r
513                 if(GetLastError() != 0)\r
514                 {\r
515                         UTIL_LogPrint( "server warning - setsockopt SO_REUSEADDR (%d)\n", GetLastError( ) );\r
516                         UTIL_LogPrint( "Binding to an in-use socket had issues. Will attempt to use the socket anyway.\n");\r
517                 }\r
518         }\r
519 \r
520         // Forcibly Disable the Nagle Algorithm, Speeding up announce requests on Windows systems.\r
521         if( setsockopt( sckListener, SOL_SOCKET, TCP_NODELAY, on, sizeof( on ) ) == SOCKET_ERROR )\r
522                 UTIL_LogPrint( "Server Warning: Disabling of Nagle Failed - %d \n", GetLastError( ) );\r
523 \r
524         if( bind( sckListener, (struct sockaddr *)&sin, sizeof( sin ) ) == SOCKET_ERROR )\r
525         {\r
526                 UTIL_LogPrint( "server error - unable to bind socket (error %d)\n", GetLastError( ) );\r
527 \r
528                 int rc = closesocket( sckListener );\r
529 \r
530                 if( rc == SOCKET_ERROR )\r
531                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
532                 else\r
533                 {\r
534                         if( gbDebug )\r
535                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
536                 }\r
537 \r
538                 return false;\r
539         }\r
540 #endif\r
541 \r
542         // listen, queue length 8\r
543 \r
544         if( listen( sckListener, 8 ) == SOCKET_ERROR )\r
545         {\r
546                 UTIL_LogPrint( "server error - unable to listen (error %d)\n", GetLastError( ) );\r
547 \r
548                 int rc = closesocket( sckListener );\r
549 \r
550                 if( rc == SOCKET_ERROR )\r
551                         UTIL_LogPrint( "server error - Closing Socket (%d)\n", GetLastError( ) );\r
552                 else\r
553                 {\r
554                         if( gbDebug )\r
555                                 UTIL_LogPrint( "server - Socket Closed\n" );\r
556                 }\r
557 \r
558                 return false;\r
559         }\r
560         m_vecListeners.push_back( sckListener );\r
561 return true;\r
562 }