From 6fde6b0fe90abde84011202edd40fe46eb06af44 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 21 Nov 2010 16:20:55 +0000 Subject: --- src/sock/sock.cpp | 661 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 382 insertions(+), 279 deletions(-) (limited to 'src/sock/sock.cpp') diff --git a/src/sock/sock.cpp b/src/sock/sock.cpp index fdba716..45583e3 100644 --- a/src/sock/sock.cpp +++ b/src/sock/sock.cpp @@ -1,12 +1,11 @@ /*:* *: File: ./src/sock/sock.cpp *: - *: yChat; Homepage: ychat.buetow.org; Version 0.9.0-CURRENT + *: yChat; Homepage: www.yChat.org; Version 0.8.3-CURRENT *: *: Copyright (C) 2003 Paul C. Buetow, Volker Richter *: Copyright (C) 2004 Paul C. Buetow *: Copyright (C) 2005 EXA Digital Solutions GbR - *: Copyright (C) 2006, 2007 Paul C. Buetow *: *: This program is free software; you can redistribute it and/or *: modify it under the terms of the GNU General Public License @@ -30,48 +29,116 @@ #include #include #include -#include #include "sock.h" -#include "../tool/tool.h" using namespace std; -vector< sock* > sock::vec_socks; -int sock::i_id_counter = 0; - sock::sock() { - this->i_id = ++i_id_counter; - this->b_run = true; this->i_req = 0; - this->p_reqp = new reqp(); + this->req_parser = new reqp(); #ifdef LOGGING - this->log_daemon = new logd( wrap::CONF->get_elem( "httpd.logging.accessfile" ), + this->log_daemon = new logd( wrap::CONF->get_elem( "httpd.logging.accessfile" ), wrap::CONF->get_elem( "httpd.logging.access_lines" ) ); #endif - this->p_ip_cache_map = new shashmap < string, unsigned, self_hash, equals_allocator >; - vec_socks.push_back(this); + this->ip_cache_map = new shashmap + < string, unsigned, self_hash, equals_allocator >; +} + +int +sock::_send(_socket *p_sock, const char *sz, int len) +{ +#ifdef OPENSSL + return send(p_sock->i_sock, sz, len, 0); +#else + return send(*p_sock, sz, len, 0); +#endif +} + +int +sock::_read(_socket *p_sock, char *sz, int len) +{ +#ifdef OPENSSL + return read(p_sock->i_sock, sz, len); +#else + return read(*p_sock, sz, len); +#endif +} + +int +sock::_close(_socket *p_sock) +{ +#ifdef OPENSSL + shutdown( p_sock->i_sock, 2 ); + close ( p_sock->i_sock ); +#else + shutdown( *p_sock, 2 ); + close ( *p_sock ); +#endif + delete p_sock; } //<<* -/* void -sock::chat_stream(context *p_context, string s_msg) +sock::chat_stream( _socket *p_sock, user *p_user, map &map_params ) { - p_user->set_context(p_sock); + string s_msg( "\n" ); + + for ( int i = 0; i < PUSHSTR; i++ ) + _send(p_sock,s_msg.c_str(), s_msg.size()); - _send(p_sock, s_msg.c_str(), s_msg.size()); // FOOBAR + pthread_mutex_t mutex; + pthread_mutex_init( &mutex, NULL ); + pthread_mutex_lock( &mutex ); - for (int i = 0; i < PUSHSTR; ++i) - _send(p_sock, " \n", 2); + do + { + s_msg = p_user->get_mess( ); - p_user->set_sock(p_sock); + if ( 0 > _send( p_sock, s_msg.c_str(), s_msg.size() ) ) + { + p_user->set_online( false ); + break; + } + + pthread_cond_wait( &(p_user->cond_message), &mutex ); + } + while( p_user->get_online() ); + + pthread_mutex_destroy( &mutex ); + + // if there is still a message to send: + s_msg = p_user->get_mess( ); + + if ( ! s_msg.empty() ) + _send( p_sock, s_msg.c_str(), s_msg.size()); + + // remove the user from its room. + string s_user( p_user->get_name() ); + string s_user_lowercase( p_user->get_lowercase_name() ); + + room* p_room = p_user->get_room(); + p_room->del_elem( s_user_lowercase ); + + // post the room that the user has left the chat. + s_msg = wrap::TIMR->get_time() + " " + + p_user->get_colored_bold_name() + + wrap::CONF->get_elem( "chat.msgs.userleaveschat" ) + + "
\n"; + + p_room->msg_post( &s_msg ); + p_room->reload_onlineframe(); + +#ifdef VERBOSE + cout << REMUSER << s_user << endl; +#endif + + wrap::GCOL->add_user_to_garbage( p_user ); } -*/ //*>> int @@ -81,366 +148,402 @@ sock::_make_server_socket( int i_port ) struct sockaddr_in name; // create the server socket. - i_sock = socket (AF_INET, SOCK_STREAM, 0); - // i_sock = socket (PF_INET, SOCK_STREAM, 0); + i_sock = socket (PF_INET, SOCK_STREAM, 0); if (i_sock < 0) { - wrap::system_message(SOCKERR, errno); + wrap::system_message( SOCKERR ); if ( ++i_port > MAXPORT ) exit(1); - wrap::system_message(SOCKERR, errno); + wrap::system_message( SOCKERR ); return _make_server_socket( i_port ); } // give the server socket a name. name.sin_family = AF_INET; - name.sin_addr.s_addr = htonl(INADDR_ANY); name.sin_port = htons(i_port); + name.sin_addr.s_addr = htonl(INADDR_ANY); int i_optval = 1; - if (bind(i_sock, (struct sockaddr *) &name, sizeof (name)) < 0) + setsockopt( i_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i_optval, sizeof(int) ); + + if ( bind(i_sock, (struct sockaddr *) &name, sizeof (name)) < 0 ) { - wrap::system_message(BINDERR, errno); + wrap::system_message( BINDERR ); - if (++i_port > MAXPORT) + if ( ++i_port > MAXPORT ) exit(1); - wrap::system_message(string(SOCKERR) + tool::int2string(i_port)); + wrap::system_message( string(SOCKERR) + tool::int2string(i_port) ); - // Re-run recursive. - return _make_server_socket(i_port); - } - - setsockopt(i_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i_optval, sizeof(int)); - - if (set_nonblock(i_server_sock) < 0) - { - wrap::system_message(NONBLER, errno); - exit(EXIT_FAILURE); + // Rerun recursive. + return _make_server_socket( i_port ); } - wrap::system_message(SOCKCRT + string("localhost:") + tool::int2string(i_port)); + wrap::system_message( SOCKCRT + string("localhost:") + tool::int2string(i_port) ); i_server_port = i_port; i_server_sock = i_sock; - if (listen(i_sock, BACKLOG) < 0) - { - wrap::system_message(LISTERR, errno); - exit(EXIT_FAILURE); - } - - wrap::system_message(SOCKRDY); return i_sock; } -#ifdef OPENSSL -// This method is virtual, and is overloaded by sslsock! -bool -sock::_main_loop_do_ssl_stuff(int &i_new_sock) +string +sock::read_http_line(_socket *p_sock) { - return 0; -} -#endif + string s_line; + int i_total = 0; + int i_read = 0; + char ch; -void -sock::process_request() -{ - int i; - struct sockaddr_in clientname; - socklen_t size; - - ++i_req; - int i_client_sock = accept(i_server_sock, (struct sockaddr *) &clientname, &size); - - if (i_client_sock == -1) + do { - switch (errno) - { - case EAGAIN: - case EINTR: - return; - } - } + i_read = _read(p_sock, &ch, sizeof(char)); - if (0 < set_nonblock(i_client_sock)) - { - wrap::system_message(NONBCER, errno); - } - -#ifdef OPENSSL - _main_loop_do_ssl_stuff(i_client_sock); -#endif + if(i_read <= 0) + return ""; -#ifdef VERBOSE - wrap::system_message(NEWREQU - + tool::int2string(i_req) + " " - + string(inet_ntoa( clientname.sin_addr )) + ":" - + tool::int2string(ntohs ( clientname.sin_port )) - + " @" + tool::int2string(get_id()) - ); -#endif + s_line += ch; + i_total++; + } + while((ch != '\n') && i_total < MAXLENGTH); - context *p_context = new context(this, new struct event, i_client_sock); + if(ch != '\n') + /* + ** the games people play + */ + return ""; - event_set(p_context->p_event, i_client_sock, EV_READ , handle_client_read, p_context); - event_add(p_context->p_event, NULL); + return s_line; } - -void -sock::clean_ipcache() +int +sock::read_http(_socket *p_sock, char *c_zbuf, int &i_postpayloadoffset) { - int i_ipcachesize = wrap::CONF->get_int("httpd.ipcachesize"); - int i_currentsize = p_ip_cache_map->size(); - - if (i_currentsize > 0 && (i_ipcachesize == 0 || i_ipcachesize <= i_currentsize)) - wrap::system_message(SOCKCA2+tool::int2string(i_currentsize)+","+tool::int2string(i_ipcachesize)+")"); -} + /* + ** 1) Read the first line + ** 2) If GET, handle as such + ** 3) If POST, handle as such + */ + char ch; + int i_read; + int i_ret = -1; + int x,z; + + string s_content_length; + string s_cl; + string s_post_return; + string s_line = read_http_line(p_sock); + + i_postpayloadoffset = 0; + if(s_line.empty()) { + wrap::system_message(SOCKER4); + return -1; + } -void -sock::init_event_handlers() -{ - for (vector< sock* >::iterator iter = sock::vec_socks.begin(); - iter != sock::vec_socks.end(); ++iter) + /* + ** GET yada\r\n Followed by stuff we don't care about :) heh. + ** 01234 + */ + /* + ** POST yada\r\n + ** xxxxx + ** Content-Length: NNN\n + ** \n + */ + if(s_line.substr(0, 3) == "GET") { - wrap::system_message(EVENTSO, (*iter)->get_id()); - (*iter)->init_server_event_handler(); + if(s_line.length() > READSOCK) + /* + ** Buffer overflow + */ + s_line = s_line.substr(0, READSOCK); + + memcpy(c_zbuf,s_line.c_str(),s_line.length()); + return s_line.length(); } -} -void -sock::init_server_event_handler() -{ - event_set(&ev_handle_server_accept, i_server_sock, EV_READ | EV_PERSIST, handle_server_accept, this); - event_add(&ev_handle_server_accept, NULL); -} - -void -sock::handle_server_accept(int i_fd, short event, void *p_arg) -{ - sock *p_sock = static_cast(p_arg); - p_sock->process_request(); -} + else + { + /* + ** POST yada + ** 01234 + */ + if(s_line.substr(0, 4) != "POST") { + wrap::system_message(SOCKER4); + return -1; + } -void -sock::handle_client_read(int i_fd, short event, void *p_arg) -{ - static int i_size = READSOCK * sizeof(char); - context *p_context = static_cast(p_arg); + /* + ** Get us to the Content-Length: + */ + s_post_return += s_line; + i_postpayloadoffset += s_line.length(); - if (-1 == read(i_fd, p_context->c_buf, i_size)) - { - switch (errno) + for(x =0; x < MAXLINES; ++x) { - case EAGAIN: - case EINTR: - event_add(p_context->p_event, NULL); - return; + s_line = read_http_line(p_sock); + s_post_return += s_line; + i_postpayloadoffset += s_line.length(); + + if (s_line.compare(0, 15, "Content-Length:")) + continue; + + // Match found on Content-Length:... process, and then break out and get us to the promised land + s_content_length = s_line.substr( 16 /*strlen("Content-Length: ")*/, + s_line.length() - 16 /*strlen("Content-Length: ")*/); + + /* + ** Content-Length: 333\n + ** 0123456789abcdefghijklmnopqrstuvwxyzAB + */ + z = 0; + + do + { + ch = s_content_length[z]; + if(isdigit(ch)) + s_cl += ch; + + ++z; + } + while(ch != '\n'); + + break; } - } - p_context->del_event(); - string s_buf(p_context->c_buf); - string s_query(""); - - bool b_is_post_request; + if(s_cl.empty()) { + wrap::system_message(SOCKER4); + return -1; + } - if (strncmp("POST", p_context->c_buf, 4) == 0) - { - b_is_post_request = true;; + z = atoi(s_cl.c_str()); - int i_pos = s_buf.find(" HTTP", 0) + 1; + /* + ** If we are going to overflow the buffer just by the payload, leave + ** of, if z did not convert correctly. (should have been ok by isdigit) + */ + if(z > READSOCK || z < 0) { + wrap::system_message(SOCKER4); + return -1; + } - if (i_pos == string::npos && i_pos <= 5) + /* + ** We have MAXLINES to get to the blank line separating POST data. + */ + for(x = 0; x < MAXLINES; ++x) { - wrap::system_message(HTTPERR); - delete p_context; - return; - } + s_line = read_http_line(p_sock); + s_post_return += s_line; - s_query.append(s_buf.substr(5, i_pos - 5)); - } + i_postpayloadoffset += s_line.length(); + if(s_line == "\r\n") + break; + } - else if (strncmp("GET", p_context->c_buf, 3) == 0) - { - b_is_post_request = false; - int i_pos = s_buf.find(" HTTP", 0) + 1; + /* + ** funny business + */ + if(x == MAXLINES) { + wrap::system_message(SOCKER4); + return -1; + } - if (i_pos == string::npos && i_pos <= 5) + for(x = 0; x < z; ++x) { - wrap::system_message(HTTPERR); - delete p_context; - return; + if(_read(p_sock,&ch,sizeof(ch)) != 1) { + wrap::system_message(SOCKER4); + return -1; + } + + s_post_return += ch; } - s_query.append(s_buf.substr(5, i_pos - 5)); - } + if(s_post_return.length() > READSOCK) { + wrap::system_message(SOCKER4); + return -1; + } - // Invalid request - else - { - wrap::system_message(HTTPERR); - delete p_context; - return; + memcpy(c_zbuf,s_post_return.c_str(),s_post_return.length()); + return s_post_return.length(); } +} - // Remove /.. from the query - int i_pos, i_pos2; - while ( (i_pos = s_query.find("/..")) != string::npos ) - s_query.replace(i_pos, 3, "/"); - - sock *p_sock = p_context->p_sock; - - p_context->p_map_params = new map; - map& map_params = *p_context->p_map_params; +int +sock::read_write(_socket* p_sock) +{ + int i_postpayloadoffset; - struct sockaddr_in client; - static socklen_t client_size = sizeof(client); - getpeername(i_fd, (struct sockaddr *) &client, &client_size); +#ifdef OPENSSL + int i_sock = p_sock->i_sock; +#else + int i_sock = *p_sock; +#endif - shashmap< string, unsigned, self_hash, equals_allocator > *p_ip_cache_map - = p_sock->get_ip_cache_map(); + char c_req[READSOCK+1]; + memset(c_req, 0, sizeof(c_req)); - unsigned &i_addr = client.sin_addr.s_addr; + int i_bytes = read_http(p_sock, c_req, i_postpayloadoffset); - if ( (map_params["REMOTE_ADDR"] = p_ip_cache_map->get_elem(i_addr)) == "" ) + if (i_bytes <= 0) { - map_params["REMOTE_ADDR"] = string(inet_ntoa(client.sin_addr)); - p_ip_cache_map->set_elem(map_params["REMOTE_ADDR"], i_addr); - wrap::system_message(SOCKCAC+map_params["REMOTE_ADDR"]); + wrap::system_message( READERR ); + } - // Get HTTP Header values - i_pos = s_buf.find("\n"); - if (i_pos != string::npos) + else { - map_params["QUERY_STRING"] = tool::trim(s_buf.substr(0,i_pos-1)); + // stores the request params. + map map_params; - do - { - string s_line(s_buf.substr(0, i_pos)); - i_pos2 = s_line.find(":"); - - if (i_pos2 != string::npos && s_line.length() > i_pos2+1) - map_params[ tool::trim(s_line.substr(0, i_pos2)) ] = tool::trim(s_line.substr(i_pos2+1)); + // get the s_rep ( s_html response which will be send imediatly to the client + struct sockaddr_in client; + size_t size = sizeof(client); - s_buf = s_buf.substr(s_line.size() + 1); - i_pos = s_buf.find("\n"); + getpeername(i_sock, (struct sockaddr *)&client, &size); + unsigned &s_addr = client.sin_addr.s_addr; + if ( (map_params["REMOTE_ADDR"] = ip_cache_map->get_elem(s_addr)) == "" ) + { + map_params["REMOTE_ADDR"] = string(inet_ntoa(client.sin_addr)); + ip_cache_map->set_elem(map_params["REMOTE_ADDR"], s_addr); + wrap::system_message(SOCKCAC+map_params["REMOTE_ADDR"]); } - while (i_pos != string::npos); - } - // Get request string parameters - string s_parameters = tool::url_decode(s_query); - string s_tmp, s_request(""); + string s_rep = req_parser->parse(p_sock, string(c_req), map_params, i_postpayloadoffset); - if ( (i_pos = s_parameters.find("?")) != string::npos) - { - s_request.append(s_parameters.substr(0, i_pos)); - s_parameters = s_parameters.substr(i_pos+1); - } +#ifdef LOGGING + log_daemon->log_access(map_params); +#endif - else if ( (i_pos = s_parameters.find(" ")) != string::npos) - { - s_request.append(s_parameters.substr(0, i_pos)); - } + // send s_rep to the client. + _send(p_sock, s_rep.c_str(), s_rep.size()); - else - { - s_request.append(s_parameters); - } + // dont need those vals anymore. + map_params.clear(); - if (b_is_post_request) - s_parameters = s_buf; + _close(p_sock); + return 0; + } - while ( (i_pos = s_parameters.find("&")) != string::npos ) - { - s_tmp = s_parameters.substr(0, i_pos ); + _close(p_sock); + return 1; +} - if ( (i_pos2 = s_tmp.find("=")) != string::npos ) - map_params[ s_tmp.substr(0, i_pos2) ] = tool::replace( s_tmp.substr( i_pos2+1 ), "\\AND", "&"); +void +sock::_main_loop_init() +{ + wrap::system_message(SOCKUNS); +} - s_parameters = s_parameters.substr(i_pos+1); - } +#ifdef OPENSSL +// This method is virtual, and is overloaded by sslsock! +bool +sock::_main_loop_do_ssl_stuff(int &i_new_sock) +{ + return 0; +} +#endif - // Get the last request parameter, which does not have a "&" on the end! - if ( (i_pos = s_parameters.find("=")) != string::npos ) - { - if ( (i_pos2 = s_parameters.find(" ")) != string::npos ) - map_params[ s_parameters.substr(0, i_pos) ] = s_parameters.substr(i_pos+1, i_pos2-i_pos-1); - else - map_params[ s_parameters.substr(0, i_pos) ] = s_parameters.substr(i_pos+1); - } +_socket* +sock::_create_container(int &i_sock) +{ + _socket* p_sock = new _socket; -#ifdef VERBOSE - wrap::system_message(REQUEST + s_request); +#ifdef OPENSSL + p_sock->i_sock = i_sock; +#else + *p_sock = i_sock; #endif + return p_sock; +} - if (s_request.empty()) - s_request = wrap::CONF->get_elem("httpd.startsite"); +int +sock::start() +{ + wrap::system_message( SOCKSRV ); + int i_sock = i_server_sock; - map_params["request"] = s_request; + int i_port = tool::string2int( wrap::CONF->get_elem("httpd.serverport") ); + _main_loop_init(); + int i; + fd_set active_fd_set, read_fd_set; + struct sockaddr_in clientname; + size_t size; + + if (listen (i_sock, 1) < 0) { - string s_ext(tool::get_extension(s_request)); + wrap::system_message( LISTERR ); + exit( EXIT_FAILURE ); + } - if ( s_ext == "" ) - s_ext = "default"; + wrap::system_message( SOCKRDY ); - map_params["content-type"] = wrap::CONF->get_elem( "httpd.contenttypes." + s_ext ); - } + // initialize the set of active sockets. + FD_ZERO (&active_fd_set); + FD_SET (i_sock, &active_fd_set); -//#ifdef VERBOSE -// for (map::iterator i = map_params.begin(); i != map_params.end(); ++i) -// cout << "=>" << i->first << "=" << i->second << "<=" << endl; -//#endif + while(b_run) + { + // block until input arrives on one or more active sockets. + read_fd_set = active_fd_set; + if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) + { + wrap::system_message( SELCERR ); + exit(EXIT_FAILURE); + } - p_context->p_response = new string(""); - p_sock->get_req_parser()->parse(p_context); + // service all the sockets with input pending. + for (i = 0; i < FD_SETSIZE; ++i) + if (FD_ISSET(i, &read_fd_set)) + { + if (i == i_sock) + { + ++i_req; // connection request on original socket. - struct event *p_ev_handle_client_write = new struct event; - p_context->p_event = p_ev_handle_client_write; + int i_new_sock; + size = sizeof(clientname); + i_new_sock = accept(i_sock, (struct sockaddr *) &clientname, &size); - event_set(p_ev_handle_client_write, i_fd, EV_WRITE, handle_client_write, p_context); - event_add(p_ev_handle_client_write, NULL); -} +#ifdef OPENSSL + if (_main_loop_do_ssl_stuff(i_new_sock)) + continue; +#endif -void -sock::handle_client_write(int i_fd, short event, void *p_arg) -{ - static int i_char_size = sizeof(char); +#ifdef VERBOSE + wrap::system_message(NEWREQU + + tool::int2string(i_req) + " " + + string(inet_ntoa( clientname.sin_addr )) + ":" + + tool::int2string(ntohs ( clientname.sin_port )) + ); +#endif - context *p_context = static_cast(p_arg); - string *p_response = p_context->p_response; + FD_SET(i_new_sock, &active_fd_set); - if (-1 == write(i_fd, p_response->c_str(), p_response->length()*i_char_size)) - { - switch (errno) - { - case EAGAIN: - case EINTR: - event_add(p_context->p_event, NULL); - return; - } + } + else + { + _socket *p_sock = _create_container(i); + pool::run( (void*) p_sock ); + FD_CLR( i, &active_fd_set ); + } + } } - - delete p_context; } -int -sock::set_nonblock(int i_sock) +void +sock::clean_ipcache() { - if (fcntl(i_sock, F_SETFL, O_NONBLOCK) < 0) + int i_ipcachesize = wrap::CONF->get_int("httpd.ipcachesize"); + int i_currentsize = ip_cache_map->size(); + + if ( i_currentsize > 0 && (i_ipcachesize == 0 || i_ipcachesize <= i_currentsize) ) { - wrap::system_message(SOCKER5, errno); - return -1; + wrap::system_message( + SOCKCA2+tool::int2string(i_currentsize)+","+tool::int2string(i_ipcachesize)+")"); } - - return 0; } #endif -- cgit v1.2.3