PageRenderTime 61ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/sources/gserver.cpp

https://github.com/andreisavu/static-httpd
C++ | 704 lines | 432 code | 105 blank | 167 comment | 59 complexity | d567f78a5ecb7adeaaba8c5ef221165c MD5 | raw file
  1. /*
  2. (c) GarajCode 2005
  3. Simple Multithreaded/Multiclient server
  4. Date : 30 iunie 2005
  5. Last date : 23 februarie 2006
  6. */
  7. /*
  8. Change log :
  9. 10 august 2005 - adaugat suport pentru optiuni de securitate ( MCSC )
  10. 11 august 2005 - suport pentru trusted ip
  11. - suport pentru banned ip
  12. 23 august 2005 - finisari
  13. 6 septembrie 2005 - modificare interfata clasa
  14. 7 septembrie 2005 - utilizare arbori binari la cautare IP
  15. 31 octombrie 2005 - WIN9X_HACK
  16. 23 februarie 2006 - comentarii doxygen - mici schimbari de interfata
  17. */
  18. #include "gserver.h"
  19. #include <stdio.h>
  20. #include <process.h>
  21. // dezactiveaza avertizarile pentru STL
  22. #pragma warning(disable:4786)
  23. #pragma warning(disable:4786)
  24. // proiectul threbuie setat pentru aplicatii multithread
  25. #ifndef _MT
  26. #error Please set your compiler to generate Multithreaded aplications.
  27. #endif
  28. #define CLOSE_WAIT_TIME 500 ///< Timpul astepta inainte ca thread-ul listen sa fie inchis fortat
  29. /// Garaj Network Namespace
  30. namespace GN
  31. {
  32. /// Cautare caracter in sir
  33. inline int is_in( char * buffer, char ch )
  34. {
  35. char *p = buffer;
  36. while( *p )
  37. {
  38. if( *p == ch ) return 1;
  39. p++;
  40. }
  41. return 0;
  42. }
  43. /// Functie auxiliara folosita pentru impartire textelor
  44. int get_token( char * buffer, char * text, int start, int end, char * sep )
  45. {
  46. int k=0;
  47. int i = start;
  48. // trece peste caracterele initiale
  49. while( is_in(sep, text[i]) && i<end ) i++;
  50. // transfera continutul in buffer
  51. while( !is_in(sep, text[i]) && i < end )
  52. {
  53. buffer[k++] = text[i];
  54. i++;
  55. }
  56. buffer[k] = 0;
  57. return i;
  58. }
  59. /// Initializare server
  60. CServer::CServer() :
  61. m_listen_thread( NULL )
  62. {
  63. }
  64. /// Inchidere automata server
  65. CServer::~CServer()
  66. {
  67. close();
  68. }
  69. /// Adauga lista de ip de incredere
  70. /**
  71. Adauga lista de IP-uri a caror conectare
  72. este permisa. IP-urile din lista sunt separate
  73. prin ;. Filtrarea este facuta automat de server.
  74. */
  75. void CServer::add_tiplist( char * list )
  76. {
  77. // proceseaza lista de ip-uri si adauga
  78. // in lista de ip-uri de incredere
  79. char buffer[50];
  80. char sep[] = "; ";
  81. int l = strlen( list );
  82. int st = -1;
  83. if( !l ) return;
  84. do
  85. {
  86. st++;
  87. st = get_token( buffer, list, st, l, sep);
  88. if( strlen(buffer)>= 7 ) add_trusted_ip( pack_ip(buffer) );
  89. }while( st != l );
  90. }
  91. /// Adauga lista de ip-uri interzise
  92. /**
  93. Adauga lista de IP-uri a caror conectare este
  94. interzisa. Trebuie sa fie separate prin ;.
  95. Filtrarea este facuta transparent de catre server.
  96. */
  97. void CServer::add_bannedlist( char * list )
  98. {
  99. char buffer[50];
  100. char sep[] = "; ";
  101. int l = strlen( list );
  102. int st = -1;
  103. if( !l ) return;
  104. do
  105. {
  106. st++;
  107. st = get_token( buffer, list, st, l, sep);
  108. if( strlen(buffer)>= 7 ) add_banned_ip( pack_ip(buffer) );
  109. }while( st != l );
  110. }
  111. /// Informatii pentru thread-ul server
  112. /**
  113. Informatii necesare pentru thread-ul de server
  114. structura este folosita doar intern pentru
  115. trimiterea de informatii intre thread-uri.
  116. */
  117. struct ServerData
  118. {
  119. ClientList *client_list; ///< Lista de clienti conectati
  120. CSocket *socket; ///< Server or client socket
  121. SERVER_PROCEDURE action; ///< Functia pentru client
  122. SERVER_EVENT_HANDLER event_handler; ///< Gestionare evenimente interne server
  123. UINT max_client_number; ///< Numarul maxim de clienti conectati simultan
  124. UINT mcsc; ///< Accepta sau nu mai multe conexiuni de la acelasi IP simultan
  125. UINT utip; ///< Foloseste sau nu lista de IP-uri de incredere
  126. TrustedIPList *tip_list; ///< Lista de IP-uri de incredere impachetate sub forma de numere
  127. UINT ubip; ///< Foloseste sau nu lista de IP-uri interzise
  128. BannedIPList *bip_list; ///< Lista de IP-uri a caror conectare nu este permisa
  129. };
  130. /// Client thread
  131. /**
  132. Serverul se bazeaza pe o arhitectura multiclient\multithreading si
  133. din acest motiv fiecare nou client este gestiona de un thread
  134. separat. Acest tip de arhitectura desi mai putin eficient datorita
  135. resurselor folosite de thread-uri si timpului necesar pentru crearea
  136. unui nou thread permite implementarea foarte usoara a protocoalelor de
  137. comunicatii. Codul pentru comunicatii este furnizat de programator
  138. printr-o functie externa serverului. Aici este apelata acea functie.
  139. */
  140. DWORD WINAPI ClientThread( LPVOID param )
  141. {
  142. ServerData *data=(ServerData*)param;
  143. // apeleaza functia care sa gestioneze clientul
  144. if(data->action)
  145. {
  146. try
  147. {
  148. data->action(*(data->client_list),*(data->socket));
  149. }
  150. catch( GSTD::CError error )
  151. {
  152. if( data->event_handler )
  153. {
  154. // semnaleaza eroare aparuta in functia de protocol
  155. data->event_handler( SM_CLIENT_ERROR, &error, data->socket );
  156. }
  157. }
  158. }
  159. // extrage adresa din lista
  160. data->client_list->remove( data->socket );
  161. // semnaleaza deconectare client
  162. if( data->event_handler )
  163. {
  164. data->event_handler( SM_CLIENT_DISCONNECTED, NULL, data->socket);
  165. }
  166. // inchide socket-ul
  167. try
  168. {
  169. data->socket->close();
  170. }
  171. catch( GSTD::CError error)
  172. {
  173. if( data->event_handler )
  174. {
  175. // semnaleaza eroare la inchidere
  176. data->event_handler( SM_CLIENT_ERROR, &error, data->socket );
  177. }
  178. }
  179. // elibereaza memoria folosita de client
  180. delete data->socket;
  181. delete data;
  182. return 0;
  183. }
  184. #ifdef WIN9X_HACK
  185. // necesara deoarece exista o problema cu
  186. // functia CreateThread pe win9x - returneaza NULL
  187. void ClientThread_Hack( void * param )
  188. {
  189. ClientThread( param );
  190. }
  191. #endif
  192. /// Listen Thread
  193. /**
  194. Acesta este thread-ul server. Atunci cand apare un nou client
  195. aceasta functie creeaza un nou thread pentru a-l gestiona. Filtrarea
  196. conexiunilor dupa IP se face aici. Orice fel de eroarea aparute
  197. in interiorul acestei functii este semnalata folosind functia
  198. furnizata ca parametru pentru evenimente de la server.
  199. */
  200. DWORD WINAPI ListenThread( LPVOID param )
  201. {
  202. ServerData *data=(ServerData*)param;
  203. ServerData *client;
  204. CSocket aux;
  205. // semnaleaza pornirea daemonului
  206. if( data->event_handler )
  207. {
  208. data->event_handler( SM_CREATE, NULL, data->socket);
  209. }
  210. while( true )
  211. {
  212. // asteapta o conexiune
  213. try
  214. {
  215. aux = data->socket->accept();
  216. }
  217. catch(GSTD::CError error)
  218. {
  219. if( data->event_handler)
  220. {
  221. // semnaleaza problema aparuta
  222. data->event_handler( SM_CONNECTION_ERROR, &error, NULL);
  223. }
  224. break;
  225. }
  226. // use banned ip
  227. if( data->ubip )
  228. {
  229. // verifica daca ip-ul se afla in lista
  230. // de ip-uri interzise
  231. if( data->bip_list->count( aux.get_ip_pack() ) )
  232. {
  233. // semnaleaza tentativa de conectare de la un IP
  234. // din lista de interzis
  235. if( data->event_handler )
  236. {
  237. data->event_handler( SM_BIP_ERROR, NULL, &aux );
  238. }
  239. aux.close();
  240. continue;
  241. }
  242. }
  243. // use trusted ip
  244. if( data->utip )
  245. {
  246. // verifica daca ip-ul este in lista
  247. // daca nu a fost gasit conexiunea este inchisa si se
  248. // asteapta alt client
  249. if( !data->tip_list->count( aux.get_ip_pack() ) )
  250. {
  251. // semnaleaza eroare
  252. if( data->event_handler )
  253. {
  254. data->event_handler( SM_TIP_ERROR, NULL, &aux );
  255. }
  256. aux.close();
  257. continue;
  258. }
  259. }
  260. // multiple connexions from same client
  261. if( !data->mcsc )
  262. {
  263. // verifica daca acest client mai este conectat odata
  264. for( ClientListIterator it = data->client_list->begin();
  265. it != data->client_list->end(); it++)
  266. if( aux.get_ip_pack() == (*it)->get_ip_pack() )
  267. {
  268. // semnaleaza eroare
  269. if( data->event_handler )
  270. {
  271. data->event_handler( SM_MCSC_ERROR, NULL, &aux );
  272. }
  273. // inchide conexiune
  274. aux.close();
  275. break;
  276. }
  277. // daca a fost gasit se asteapta o alta conexiune
  278. if( it != data->client_list->end() ) continue;
  279. }
  280. // verifica limita pentru clienti
  281. if( data->client_list->size() >= data->max_client_number )
  282. {
  283. // semnaleaza depasire limita de conexiuni simultane
  284. if(data->event_handler)
  285. {
  286. data->event_handler(SM_CLIENT_LIMIT_REACHED, NULL, &aux);
  287. }
  288. // refuza conexiunea curenta
  289. try
  290. {
  291. aux.close();
  292. }
  293. catch( GSTD::CError error )
  294. {
  295. if( data->event_handler )
  296. {
  297. // semnaleaza eroare
  298. data->event_handler( SM_CLIENT_ERROR, &error, &aux);
  299. }
  300. }
  301. // asteapta pentru urmatorul client
  302. continue;
  303. }
  304. // semnaleaza conectarea unui nou client si asteapta acceptul conexiunii
  305. if( data->event_handler )
  306. {
  307. if( !data->event_handler( SM_CLIENT_CONNECTED, NULL, &aux ) )
  308. {
  309. // conexiunea a fost refuzata
  310. try
  311. {
  312. aux.close();
  313. }
  314. catch( GSTD::CError error )
  315. {
  316. if( data->event_handler )
  317. {
  318. // semnaleaza eroare
  319. data->event_handler( SM_CLIENT_ERROR, &error, &aux);
  320. }
  321. }
  322. // asteapta urmatorul client
  323. continue;
  324. }
  325. }
  326. // aloca memorie pentru un nou client
  327. if( !(client = new ServerData) )
  328. {
  329. // problema interna cu memoria - server-ul nu trebuie inchis
  330. // semnaleaza problema
  331. if(data->event_handler)
  332. {
  333. data->event_handler(SM_NO_MEMORY_FOR_CLIENT, NULL, &aux);
  334. }
  335. // refuza conexiunea curenta din lipsa de resurse
  336. try
  337. {
  338. aux.close();
  339. }
  340. catch( GSTD::CError error )
  341. {
  342. if( data->event_handler )
  343. {
  344. // semnaleaza problema
  345. data->event_handler( SM_CLIENT_ERROR, &error, &aux);
  346. }
  347. continue;
  348. }
  349. }
  350. // seteaza structura
  351. client->action = data->action;
  352. client->client_list = data->client_list;
  353. client->event_handler = data->event_handler;
  354. // aloca memorie pentru socket
  355. if( !(client->socket = new CSocket) )
  356. {
  357. // problema cu memoria - se renunta la acest client
  358. if(data->event_handler)
  359. {
  360. data->event_handler(SM_NO_MEMORY_FOR_CLIENT, NULL, &aux);
  361. }
  362. try
  363. {
  364. aux.close();
  365. }
  366. catch( GSTD::CError error )
  367. {
  368. if( data->event_handler)
  369. {
  370. // problema de semnalat
  371. data->event_handler( SM_CLIENT_ERROR, &error, &aux);
  372. }
  373. }
  374. delete client;
  375. continue;
  376. }
  377. *client->socket = aux;
  378. // salveza adresa noului socket in lista
  379. data->client_list->push_back(client->socket);
  380. #ifndef WIN9X_HACK
  381. // creaza un nou thread pentru client
  382. HANDLE tmph = CreateThread(NULL,0,ClientThread,client,0,NULL);
  383. if( !tmph )
  384. {
  385. // nu s-a putut crea thread-ul pentru gestionarea
  386. // clientului, conexiunea este refuzata si server-ul
  387. // isi vede de treaba mai departe eroare fiind semnalata
  388. if( data->event_handler)
  389. {
  390. data->event_handler( SM_THREAD_ERROR, NULL, &aux);
  391. }
  392. try
  393. {
  394. aux.close();
  395. }
  396. catch( GSTD::CError error )
  397. {
  398. if( data->event_handler )
  399. {
  400. // semnaleaza problema
  401. data->event_handler( SM_CLIENT_ERROR, &error, &aux );
  402. }
  403. }
  404. // elibereaza memoria alocata pentru client
  405. delete client->socket;
  406. delete client;
  407. // asteapta urmatoarea conexiune
  408. continue;
  409. }
  410. // inchide handle
  411. CloseHandle( tmph );
  412. #endif
  413. #ifdef WIN9X_HACK // problema legat de CreateThread - un hack pentru WIN9X
  414. if( _beginthread( ClientThread_Hack, 0, client ) == -1L )
  415. {
  416. // nu s-a putut crea thread-ul pentru gestionarea
  417. // clientului, conexiunea este refuzata si server-ul
  418. // isi vede de treaba mai departe eroare fiind semnalata
  419. if( data->event_handler)
  420. {
  421. data->event_handler( SM_THREAD_ERROR, NULL, &aux);
  422. }
  423. try
  424. {
  425. aux.close();
  426. }
  427. catch( GSTD::CError error )
  428. {
  429. if( data->event_handler )
  430. {
  431. // semnaleaza problema
  432. data->event_handler( SM_CLIENT_ERROR, &error, &aux );
  433. }
  434. }
  435. // elibereaza memoria alocata pentru client
  436. delete client->socket;
  437. delete client;
  438. // asteapta urmatoarea conexiune
  439. continue;
  440. }
  441. #endif
  442. }
  443. // inchide toate conexiunile cu clientii si asteapta
  444. // ca lista sa devina vida
  445. for(ClientListIterator it = data->client_list->begin();
  446. it != data->client_list->end(); it++)
  447. {
  448. try
  449. {
  450. (*it)->close();
  451. }
  452. catch( GSTD::CError error )
  453. {
  454. if( data->event_handler )
  455. {
  456. //semnaleaza eroare
  457. data->event_handler( SM_CLIENT_ERROR, &error, *it);
  458. }
  459. }
  460. }
  461. // asteapta pana toti clienti se inchid
  462. // verificare se face la un interval de jumatate
  463. // de secunda pentru a evita utilizarea inutila
  464. // a procesorului
  465. while( data->client_list->size() ) Sleep(500);
  466. // semnaleaza inchiderea daemonului
  467. if( data->event_handler )
  468. {
  469. data->event_handler( SM_CLOSE, NULL, data->socket);
  470. }
  471. // elibereaza memoria necesara pentru daemon
  472. delete data;
  473. return 1;
  474. }
  475. #ifdef WIN9X_HACK
  476. // problema cu CreateThread pe WIN9X
  477. void ListenThread_Hack( void * param )
  478. {
  479. ListenThread( param );
  480. }
  481. #endif
  482. /// Initializarea serverulu
  483. /**
  484. Aceasta functia initializeaza toate structurile interne
  485. pentru un nou server dupa care creeaza thread-ul pentru
  486. ascultarea de noi conexiuni. Dupa activare functia se termina
  487. iar serverul ruleaza in paralel.
  488. */
  489. void CServer::create( ServerCreationData &init_data )
  490. {
  491. // verifica daca avem o functie pentru client
  492. if( !init_data.server_procedure )
  493. {
  494. throw GSTD::CError(0, "No client function" );
  495. }
  496. // initializeaza socket-ul de server
  497. try
  498. {
  499. m_listen_socket.create();
  500. m_listen_socket.listen( init_data.port );
  501. }
  502. catch(GSTD::CError error)
  503. {
  504. throw (GSTD::CError)error;
  505. }
  506. // aloca memorie necesara pentru server
  507. ServerData *data;
  508. if(!(data=new ServerData))
  509. {
  510. m_listen_socket.close();
  511. throw GSTD::CError( GSTD::BAD_ALLOC );
  512. }
  513. // seteaza structura care va fi pasata mai departe
  514. data->action = init_data.server_procedure; // functia pentru protocol
  515. data->event_handler = init_data.server_event_handler; // gestionare evenimente interne
  516. data->client_list = &m_client_list; // lista de clienti
  517. data->socket = &m_listen_socket; // socket-ul server
  518. data->mcsc = init_data.mcsc; // conexiuni multiple de la acelasi IP
  519. data->max_client_number = init_data.max_client_number; // numarul maxim de conexiuni simultane
  520. data->utip = init_data.utip; // use trusted ip list
  521. data->tip_list = &m_tip_list; // lista de ip-uri de incredere
  522. data->ubip = init_data.ubip; // use banned ip list
  523. data->bip_list = &m_bip_list; // lista de ip-uri interzise
  524. // porneste daemonul pentru server
  525. #ifndef WIN9X_HACK
  526. // versiunea care TEORETIC trebuie sa fie independenta de
  527. // versiunea de Windows
  528. m_listen_thread = CreateThread(NULL, 0, ListenThread, data, 0, NULL);
  529. if( !m_listen_thread )
  530. {
  531. // eroare la pornire daemon
  532. GSTD::CError error;
  533. error.set_code( GetLastError() );
  534. LPVOID lpMsgBuf;
  535. FormatMessage(
  536. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  537. NULL,
  538. error.get_code(),
  539. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  540. (LPTSTR) &lpMsgBuf,
  541. 0,
  542. NULL
  543. );
  544. // Fill error struct
  545. error.set_text( (char*)lpMsgBuf );
  546. // Free the buffer.
  547. LocalFree( lpMsgBuf );
  548. // elibereaza memoria alocata
  549. delete data;
  550. // semnaleaza eroare
  551. throw (GSTD::CError)error;
  552. }
  553. #endif
  554. #ifdef WIN9X_HACK
  555. // foloseste _beginthread
  556. // dezavantaj major - nu mai permite folosirea WaitForSingleObject
  557. if( _beginthread( ListenThread_Hack, 0, data ) == -1L )
  558. {
  559. throw GSTD::CError( 0, "Error creating listen thread - WIN9XHACK" );
  560. }
  561. #endif
  562. }
  563. /// Inchide serverul
  564. /**
  565. Pentru a inchide serverul este inchis socketul pentru server.
  566. Pentru a fi sigur ca toate resursele au fost eliberate corect
  567. si nu a ramas memorie alocata serverul nu se inchide pana
  568. in momentul in care toate thread-urile clientilor sau inchis
  569. ( dupa ce in prealabil socketurile clientilor au fost inchise ).
  570. */
  571. void CServer::close()
  572. {
  573. try
  574. {
  575. m_listen_socket.close();
  576. }
  577. catch(GSTD::CError error)
  578. {
  579. throw (GSTD::CError)error;
  580. }
  581. #ifndef WIN9X_HACK
  582. if(m_listen_thread)
  583. {
  584. WaitForSingleObject(m_listen_thread, INFINITE);
  585. m_listen_thread = NULL;
  586. }
  587. #endif
  588. #ifdef WIN9X_HACK
  589. // asteapta pana ce toti clientii se inchide
  590. while( m_client_list.size() ) Sleep( 500 );
  591. Sleep( CLOSE_WAIT_TIME );
  592. #endif
  593. // elibereaza lista de ip in care se are incredere
  594. m_tip_list.clear();
  595. // elibereaza lista de ip-uri interzise
  596. m_bip_list.clear();
  597. }
  598. }; // namespace GN
  599. // (c) GarajCode 2005-2006 - programmed by Savu Andrei