static bool _network_connect_establishedHandler(int32 fd) { register SESSION *s = &g_Session[fd]; int val; socklen_t val_len; if(s->type == NST_FREE) return true; // due to multiple non coalesced event notifications // this can happen .. when a previous handled event has already disconnected the connection // within the same cycle.. val = -1; val_len = sizeof(val); getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &val_len); if(val != 0) { // :( .. cleanup session.. s->type = NST_FREE; s->onSend = NULL; s->onConnect = NULL; s->onDisconnect = NULL; evdp_remove(fd, &s->evdp_data); close(fd); return true; // we CANT return false, // becuase the normal disconnect procedure would execute the ondisconnect handler, which we dont want .. in this case. } else { // ok if(s->onConnect(fd) == false) { // onConnect handler has refused the connection .. // cleanup .. and ok s->type = NST_FREE; s->onSend = NULL; s->onConnect = NULL; s->onDisconnect = NULL; evdp_remove(fd, &s->evdp_data); close(fd); return true; // we dnot want the ondisconnect handler to be executed, so its okay to handle this by ourself. } // connection established ! // if(evdp_outgoingconnection_established(fd, &s->evdp_data) == false) { return false; // we want the normal disconnect procedure.. with call to ondisconnect handler. } s->onSend = NULL; ShowStatus(read_message("Source.common.network_connect_establishedHandler"), fd); } return true; }//end: _network_connect_establishedHandler()
static bool _network_accept(int32 fd) { SESSION *listener = &g_Session[fd]; SESSION *s; union { struct sockaddr_in v4; #ifdef ENABLE_IPV6 struct sockaddr_in6 v6; #endif } _addr; int newfd; socklen_t addrlen; struct sockaddr *addr; // Accept until OS returns - nothing to accept anymore // - this is required due to our EVDP abstraction. (which handles on listening sockets similar to epoll's EPOLLET flag.) while(1) { #ifdef ENABLE_IPV6 if(listener->v6 == true) { addrlen = sizeof(_addr.v6); addr = (struct sockaddr *)&_addr.v6; } else { #endif addrlen = sizeof(_addr.v4); addr = (struct sockaddr *)&_addr.v4; #ifdef ENABLE_IPV6 } #endif #ifdef HAVE_ACCEPT4 newfd = accept4(fd, addr, &addrlen, SOCK_NONBLOCK); #else newfd = accept(fd, addr, &addrlen); #endif if(newfd == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) break; // this is fully valid & whished., se explaination on top of while(1) // Otherwis .. we have serious problems :( seems tahat our listner has gone away.. // @TODO handle this .. ShowError(read_message("Source.common.network_accept"), errno, strerror(errno)); return false; // will call disconnect after return. //break; } #ifndef HAVE_ACCEPT4 // no accept4 means, we have to set nonblock by ourself. .. if(_setnonblock(newfd) == false) { ShowError(read_message("Source.common.network_accept2"), errno, strerror(errno)); close(newfd); continue; } #endif // Check connection limits. if(newfd >= MAXCONN) { ShowError(read_message("network_accept3"), MAXCONN); close(newfd); continue; // we have to loop over the events (and disconnect them too ..) but otherwise we would leak event notifications. } // Create new Session. s = &g_Session[newfd]; s->type = NST_CLIENT; // The new connection inherits listenr's handlers. s->onDisconnect = listener->onDisconnect; s->onConnect = listener->onConnect; // maybe useless but .. fear the future .. :~ // Register the new connection @ EVDP if(evdp_addclient(newfd, &s->evdp_data) == false) { ShowError(read_message("Source.common.network_accept3")); close(newfd); s->type = NST_FREE; } // Call the onConnect handler on the listener. if(listener->onConnect(newfd) == false) { // Resfused by onConnect handler.. evdp_remove(newfd, &s->evdp_data); close(newfd); s->type = NST_FREE; s->data = NULL; // be on the safe side ~ ! continue; } } return true; }//end: _network_accept()
void network_disconnect(int32 fd) { SESSION *s = &g_Session[fd]; netbuf b, bn; // Prevent recursive calls // by wrong implemented on disconnect handlers.. and such.. if(s->disconnect_in_progress == true) return; s->disconnect_in_progress = true; // Disconnect Todo: // - Call onDisconnect Handler // - Release all Assigned buffers. // - remove from event system (notifications) // - cleanup session structure // - close connection. // if(s->onDisconnect != NULL && s->type != NST_LISTENER) { s->onDisconnect(fd); } // Read Buffer if(s->read.buf != NULL) { netbuffer_put(s->read.buf); s->read.buf = NULL; } // Write Buffer(s) b = s->write.buf; while(1) { if(b == NULL) break; bn = b->next; netbuffer_put(b); b = bn; } s->write.buf = NULL; s->write.buf_last = NULL; s->write.n_outstanding = 0; s->write.max_outstanding = 0; // Remove from event system. evdp_remove(fd, &s->evdp_data); // Cleanup Session Structure. s->type = NST_FREE; s->data = NULL; // no application level data assigned s->disconnect_in_progress = false; // Close connection close(fd); }//end: network_disconnect()
int32 network_connect(bool v6, const char *addr, uint16 port, const char *from_addr, uint16 from_port, bool (*onConnectionEstablishedHandler)(int32 fd), void (*onConnectionLooseHandler)(int32 fd) ){ register SESSION *s; int32 fd, optval, ret; struct sockaddr_in ip4; #ifdef ENABLE_IPV6 struct sockaddr_in6 ip6; #endif #ifdef ENABLE_IPV6 if(v6 == true) fd = socket(AF_INET6, SOCK_STREAM, 0); else #endif fd = socket(AF_INET, SOCK_STREAM, 0); #ifndef ENABLE_IPV6 // check.. if(v6 == true){ ShowError("network_connect(%c, '%s', %u...): tried to create an ipv6 connection, IPV6 is not supported in this release.\n", (v6==true?'t':'f'), addr, port); return -1; } #endif // check connection limits. if(fd >= MAXCONN){ ShowError("network_connect(%c, '%s', %u...): cannot create new connection, exceeeds more than supported connections (%u)\n", (v6==true?'t':'f'), addr, port ); close(fd); return -1; } // Originating IP/Port pair given ? if(from_addr != NULL && *from_addr != 0){ //.. #ifdef SO_REUSEADDR optval=1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); #endif #ifdef ENABLE_IPV6 if(v6 == true){ memset(&ip6, 0x00, sizeof(ip6)); ip6.sin6_family = AF_INET6; ip6.sin6_port = htons(from_port); if(inet_pton(AF_INET6, from_addr, &ip6.sin6_addr) != 1){ ShowError("network_connect(%c, '%s', %u...): cannot parse originating (from) IPV6 address (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno)); close(fd); return -1; } ret = bind(fd, (struct sockaddr*)&ip6, sizeof(ip6)); }else{ #endif memset(&ip4, 0x00, sizeof(ip4)); ip4.sin_family = AF_INET; ip4.sin_port = htons(from_port); ip4.sin_addr.s_addr = inet_addr(from_addr); ret = bind(fd, (struct sockaddr*)&ip4, sizeof(ip4)); #ifdef ENABLE_IPV6 } #endif } // Set non block if(_setnonblock(fd) == false){ ShowError("network_connect(%c, '%s', %u...): cannot set socket to nonblocking (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno)); close(fd); return -1; } // Create ip addr block to connect to .. #ifdef ENABLE_IPV6 if(v6 == true){ memset(&ip6, 0x00, sizeof(ip6)); ip6.sin6_family = AF_INET6; ip6.sin6_port = htons(port); if(inet_pton(AF_INET6, addr, &ip6.sin6_addr) != 1){ ShowError("network_connect(%c, '%s', %u...): cannot parse destination IPV6 address (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno)); close(fd); return -1; } }else{ #endif memset(&ip4, 0x00, sizeof(ip4)); ip4.sin_family = AF_INET; ip4.sin_port = htons(port); ip4.sin_addr.s_addr = inet_addr(addr); #ifdef ENABLE_IPV6 } #endif // Assign Session.. s = &g_Session[fd]; s->type = NST_OUTGOING; s->v6 = v6; s->onConnect = onConnectionEstablishedHandler; s->onDisconnect = onConnectionLooseHandler; s->onRecv = NULL; s->onSend = _network_connect_establishedHandler; #ifdef ENABLE_IPV6 if(v6 == true) memcpy(&s->addr.v6, &ip6, sizeof(ip6)); else #endif memcpy(&s->addr.v4, &ip4, sizeof(ip4)); // Register @ EVDP. as outgoing (see doc of the function) if(evdp_addconnecting(fd, &s->evdp_data) == false){ ShowError("network_connect(%c, '%s', %u...): eventdispatcher subsystem returned an error.\n", (v6==true?'t':'f'), addr, port); // cleanup session x.x.. s->type = NST_FREE; s->onConnect = NULL; s->onDisconnect = NULL; s->onSend = NULL; // close, return error code. close(fd); return -1; } #ifdef ENABLE_IPV6 if(v6 == true) ret = connect(fd, (struct sockaddr*)&ip6, sizeof(ip6)); else #endif ret = connect(fd, (struct sockaddr*)&ip4, sizeof(ip4)); // if(ret != 0 && errno != EINPROGRESS){ ShowWarning("network_connect(%c, '%s', %u...): connection failed (errno: %u / %s)\n", (v6==true?'t':'f'), addr, port, errno, strerror(errno)); // Cleanup session .. s->type = NST_FREE; s->onConnect = NULL; s->onDisconnect = NULL; s->onSend = NULL; // .. remove from evdp and close fd. evdp_remove(fd, &s->evdp_data); close(fd); return -1; } // ! The Info Message :~D ShowStatus("network_connect fd#%u (%s:%u) in progress.. \n", fd, addr, port); return fd; }//end: network_connect()