/** * $Id: small_socket.cpp,v 1.30 2002/10/31 22:34:45 plasmahh Exp $ * $Revision: 1.30 $ * $Author: plasmahh $ * $Date: 2002/10/31 22:34:45 $ */ #include "small_socket.h" BOOL small_socket::Connect ( const mstring& ipaddr, uint16 port, uint32 timeout_msec )/*{{{*/ { if (listening) { return false; } struct hostent *peerip; struct sockaddr_in peer; //save to easy reconnect peer_ip = ipaddr; peer_port = port; if (sock > 0) { close(sock); } connected = false; sock = socket ( AF_INET, SOCK_STREAM, 0); if ( socket <= 0) { last_error = errno; #ifdef _SAFE_ throw new x_com ("getting socket from system" ,__FLF__); #endif return false; } // info is static in gethostbyname, so following calls will overwrite this, not thread save !!!! peerip = gethostbyname ( ipaddr ); if (!peerip) { last_error = errno; #ifdef _SAFE_ throw new x_com ("getting host ip (possibly no dns records, or dns not working ?)" , __FLF__); #endif return false; } peer.sin_family = AF_INET; peer.sin_port = htons(port); // copy info, since everything in peer will be lost ! peer.sin_addr = *((struct in_addr *) peerip->h_addr); memset(&(peer.sin_zero),0,8); ///////////////////////// int s_options; s_options=1; // SO_KEEPALIVE Send keepalive messages through socket (for long time connections) // SO_REUSEADDR Reuse binded socket even if still in connect sttae //setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int) ); // set options to O_NONBLOCK and then connect() after this ask with select() it the fd is ready, if not in the timeout, abort the connect (throw exception ?) fcntl(sock,F_SETFL,O_NONBLOCK); //cout << "Set socket to noblocking...:"<< endl; ///////////////////////// int cono; cono = connect( sock, (sockaddr *) & peer, sizeof(struct sockaddr)); if ( cono < 0 && (errno == EINPROGRESS) ) { // wait with a select here... fd_set fdvar; timeval * timeout = new timeval; timeout->tv_sec = timeout_msec / 1000; timeout->tv_usec = 1000 * (timeout_msec % 1000); FD_ZERO ( &fdvar ); FD_SET ( sock, &fdvar ); select ( sock+1, NULL, &fdvar, NULL , timeout ); delete timeout; // If set, we may be connected after timeout, if not, then not ;) last_error = ETIMEDOUT; if ( FD_ISSET ( sock, &fdvar ) ) { int so_options=0; so_options = get_sock_error(); // only if socket options is right we are really connected if ( so_options == 0) { last_error = 0; connected = true; return true; } last_error = so_options; } close(sock); sock = -1; return false; } if ( cono == 0 ) { // we seem to be connected, should only happen on localhost connections without context change last_error = 0; connected = true; return true; } cout << "THIS SHOULD NOT HAPPEN, PLEASE INFORM THE AUTHOR !! " << cono << endl; if ( cono < 0 ) { close (sock); sock = -1; #ifdef _SAFE_ throw new x_com ("connecting to peer", __FLF__); #endif return false; } last_error = 0; connected = true; return true; }/*}}}*/ BOOL small_socket::do_listen( struct sockaddr* horchen, uint32 linger )/*{{{*/ { if (connected) { return false; } if (list_sock > 0) { close(list_sock); } listening = false; list_sock = -1; list_sock = socket ( AF_INET, SOCK_STREAM, 0); if (list_sock <= 0) { last_error = errno; return false; } listening = true; uint32 opt=0; setsockopt(list_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(uint32) ); // set socket to non-blocking !! fcntl(sock,F_SETFL,O_NONBLOCK); if ( bind(list_sock, horchen, sizeof(struct sockaddr)) == -1) { last_error = errno; close(list_sock); list_sock = -1; return false; } if ( listen(list_sock,linger) == -1) { last_error = errno; close(list_sock); list_sock = -1; return false; } return true; }/*}}}*/ BOOL small_socket::Listen ( const mstring& ipaddr, uint16 port , uint32 linger)/*{{{*/ { struct sockaddr_in horchen; struct hostent *listip; listip = gethostbyname ( ipaddr ); if ( !listip ) return false; horchen.sin_family = AF_INET; horchen.sin_port = htons(port); horchen.sin_addr = *((struct in_addr *) listip->h_addr); //horchen.sin_addr.s_addr = INADDR_ANY; return do_listen((struct sockaddr*)&horchen, linger); }/*}}}*/ BOOL small_socket::Listen ( uint16 port , uint32 linger )/*{{{*/ { struct sockaddr_in horchen; horchen.sin_family = AF_INET; horchen.sin_port = htons(port); horchen.sin_addr.s_addr = INADDR_ANY; return do_listen((struct sockaddr*)&horchen, linger); }/*}}}*/ BOOL small_socket::Listen ( uint16 port, const mstring& iface, uint32 linger)/*{{{*/ { // TODO get address of interface and then bind to it !! return Listen (port, linger); }/*}}}*/ small_socket* small_socket::Accept( BOOL closeit, uint32 timeout_msec)/*{{{*/ { //TODO Implement Timeout //TODO find a way to look who tries to connect, BEFORE accepting (to check it against db or so) struct sockaddr_in crequest; socklen_t rqlen = sizeof(sockaddr_in); sock = accept(list_sock, (struct sockaddr*) &crequest, &rqlen); if ( sock == -1 ) { connected = false; } else { connected = true; uchar8 *aptr = (uchar8 *) &(crequest.sin_addr); peer_ip.format("%d.%d.%d.%d", aptr[0], aptr[1], aptr[2], aptr[3]); } small_socket* tmp = new small_socket(*this); if(closeit) { close(list_sock); listening = false; } else { listening = true; } return tmp; }/*}}}*/ BOOL small_socket::check_read ( BOOL must, sint32 csock, uint32 timeout_msec)/*{{{*/ { if (!must) return false; if (csock <= 0) { return false; } fd_set fdvar; timeval * timeout = new timeval; timeout->tv_sec = timeout_msec / 1000; timeout->tv_usec = 1000 * (timeout_msec % 1000); FD_ZERO ( &fdvar ); FD_SET ( csock, &fdvar ); select ( csock+1,&fdvar, NULL, NULL , timeout ); delete timeout; return ( FD_ISSET ( csock, &fdvar ) ); }/*}}}*/ uint32 small_socket::get_sock_error ( void )/*{{{*/ { uint32 so_options=0; uint32 optlen=sizeof(uint32); getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&so_options, (socklen_t *)&optlen ); last_error = so_options; return last_error; }/*}}}*/ BOOL small_socket::Disconnect ( void )/*{{{*/ { // be hard and just close socket *fg* BOOL ret = true; ret &= ( close(sock) != -1); connected = false; sock= -1; return ret; }/*}}}*/ BOOL small_socket::Reconnect( uint32 timeout_msec )/*{{{*/ { //perhaps we are still connected, just close close(sock); return Connect ( peer_ip, peer_port, timeout_msec); }/*}}}*/ // TODO make this thing signal safe here mstring small_socket::Receive ( uint32 bytes ,uint32 timeout_msec)/*{{{*/ { uchar8 * rcvbuf; sint32 rd; rcvbuf = new uchar8[bytes+1]; memset (rcvbuf,0,bytes+1); rd = Receive ( rcvbuf, bytes, timeout_msec ); // am I right ? when number of read bytes is 0, but select says there is // something, then we are disconnected ?? mh, ok, and when we are not // disconncted, but select runs out, the return will be -1 but noone will // know if because of an error during read, or because of a timeout... // perhaps we should introduce excpetions to this ? So one can ignore if // there is no data waiting... mh, but if one desires to wait for data he // should use DataWaiting check then, and read after that if then theres an // error, he can be shure that its a real error, not just timeout... and if // DataWaiting returns false, there is now data waiting... if ( rd == 0 ) { // ok, we are disconnected then...so disconnect... ;) Disconnect(); } mstring g(rcvbuf); delete [] rcvbuf; return g; }/*}}}*/ sint32 small_socket::Receive ( uchar8 * buffer, uint32 bytes, uint32 timeout_msec)/*{{{*/ { if (sock <= 0) { return -1; } // Without a properly connected socket, we can't receive... fd_set fdvar; timeval * timeout = new timeval; timeout->tv_sec = timeout_msec / 1000; timeout->tv_usec = 1000 * (timeout_msec % 1000); FD_ZERO ( &fdvar ); FD_SET ( sock, &fdvar ); select ( sock+1,&fdvar, NULL, NULL , timeout ); delete timeout; if ( FD_ISSET ( sock, &fdvar ) ) { return read( sock, (void *) buffer, bytes); } // We have no data waiting after the timeout, so return an error here... return -1; }/*}}}*/ sint32 small_socket::Send ( const mstring& v, uint32 timeout_msec)/*{{{*/ { return Send((const uchar8 *)v, v.length(), timeout_msec); }/*}}}*/ sint32 small_socket::Send (const uchar8 * buffer, uint32 bytes, uint32 timeout_msec)/*{{{*/ { fd_set fdvar; if (sock <= 0 ) { return -1; } timeval * timeout = new timeval; timeout->tv_sec = timeout_msec / 1000; timeout->tv_usec = 1000 * (timeout_msec % 1000); FD_ZERO ( &fdvar ); FD_SET ( sock, &fdvar ); select ( sock+1, NULL, &fdvar, NULL , timeout ); delete timeout; sint32 wbytes; if ( FD_ISSET ( sock, &fdvar ) ) { wbytes = write( sock, buffer, bytes); return wbytes; } return -1; }/*}}}*/ mstring small_socket::get_resolved_host( const mstring& host )/*{{{*/ { // well, we don't really know if host is a IP or Hostname // so first get adress by name, and then get name by this adress struct hostent * rhost; mstring resolved; rhost = gethostbyname ((const char *) host ); if ( rhost ) { rhost = gethostbyaddr(rhost->h_addr,rhost->h_length,rhost->h_addrtype); } if ( rhost ) { resolved = rhost->h_name; } return resolved; }/*}}}*/ mstring small_socket::get_resolved_ip ( const mstring& host )/*{{{*/ { struct hostent * rhost; mstring resolved; rhost = gethostbyname((const char *) host ); if (rhost) { resolved.format("%d.%d.%d.%d", ((unsigned short) rhost->h_addr_list[0][0] & 255), ((unsigned short) rhost->h_addr_list[0][1] & 255), ((unsigned short) rhost->h_addr_list[0][2] & 255), ((unsigned short) rhost->h_addr_list[0][3] & 255) ); } return resolved; }/*}}}*/ BOOL small_socket::Accept ( uint32 timeout_msec ) /*{{{*/ { small_socket* tmp = Accept ( true, timeout_msec ); BOOL ret = tmp->Connected(); delete tmp; return ret; }/*}}}*/ /** *$Log: small_socket.cpp,v $ *Revision 1.30 2002/10/31 22:34:45 plasmahh *mstring fix * *Revision 1.29 2002/10/22 14:01:07 plasmahh *implementing binary tree * *Revision 1.28 2002/10/21 08:37:21 plasmahh *beginning cleanup for smtpmap release 0.8-stable * *Revision 1.27 2002/10/08 15:01:44 plasmahh *some changes for compatibility with sun/solaris/sparc * *Revision 1.26 2002/08/12 13:15:25 plasmahh *removed some memory leaks * *Revision 1.25 2002/08/08 22:59:08 plasmahh *output changes * *Revision 1.24 2002/08/08 22:32:19 plasmahh *fixed no-connect reason * *Revision 1.23 2002/08/08 22:14:11 plasmahh *fixed fast connection behaviour * *Revision 1.22 2002/08/08 21:28:40 plasmahh *fixed ? * *Revision 1.21 2002/08/08 21:25:00 plasmahh *msg fix * *Revision 1.20 2002/06/20 21:02:10 plasmahh *internal documentation changes *socket fixes ! * *Revision 1.19 2002/06/20 16:58:48 plasmahh *fixed & improved timeout behaviour in connect() call * *Revision 1.18 2002/06/17 22:27:10 plasmahh *fixed segfault in host resolv * *Revision 1.17 2002/06/17 14:05:06 plasmahh *resolv for IPs added * *Revision 1.16 2002/06/17 12:20:29 plasmahh *implemented conenct timeout * *Revision 1.15 2002/06/17 06:54:22 plasmahh *improved socket with connection timeout * *Revision 1.14 2002/06/13 12:30:32 plasmahh *öhm, ja * *Revision 1.13 2002/06/10 19:44:53 plasmahh *some slight compiler defined changes * *Revision 1.12 2002/06/10 11:19:42 plasmahh *Removed ??) which is a trigraph in an error message * *Revision 1.11 2002/06/10 11:15:34 plasmahh *remoed default values in the wrong place * *Revision 1.10 2002/06/09 21:23:21 plasmahh *changed a few things that might be bugs * *Revision 1.9 2002/05/27 06:24:09 plasmahh *New function in socket class to try detecting disconnects * *Revision 1.8 2002/05/23 19:48:08 plasmahh *removed some compiler warnings. *fixed one or two bugs. *refined include structure, for faster compiling *hm, what else ? don't know, I hope this works all ;) * *Revision 1.7 2002/05/15 19:40:26 plasmahh *fixed something to compile also on solaris * *Revision 1.6 2002/05/14 16:31:13 plasmahh *Removed some compiler warnings *Added more code, hopefully fast ;) * *Revision 1.5 2002/05/01 23:08:28 plasmahh *ficed * *Revision 1.4 2002/05/01 20:13:23 plasmahh *made interface to socket a bit easier for simple use *added connection exception * *Revision 1.3 2002/04/30 22:29:01 plasmahh *changed socket interface to our string class * *Revision 1.2 2002/04/16 20:07:51 plasmahh *New header added * */