void ConnectionDescriptor::Read() { /* Read and dispatch data on a socket that has selected readable. * It's theoretically possible to get and dispatch incoming data on * a socket that has already been scheduled for closing or close-after-writing. * In those cases, we'll leave it up the to protocol handler to "do the * right thing" (which probably means to ignore the incoming data). * * 22Aug06: Chris Ochs reports that on FreeBSD, it's possible to come * here with the socket already closed, after the process receives * a ctrl-C signal (not sure if that's TERM or INT on BSD). The application * was one in which network connections were doing a lot of interleaved reads * and writes. * Since we always write before reading (in order to keep the outbound queues * as light as possible), I think what happened is that an interrupt caused * the socket to be closed in ConnectionDescriptor::Write. We'll then * come here in the same pass through the main event loop, and won't get * cleaned up until immediately after. * We originally asserted that the socket was valid when we got here. * To deal properly with the possibility that we are closed when we get here, * I removed the assert. HOWEVER, the potential for an infinite loop scares me, * so even though this is really clunky, I added a flag to assert that we never * come here more than once after being closed. (FCianfrocca) */ int sd = GetSocket(); //assert (sd != INVALID_SOCKET); (original, removed 22Aug06) if (sd == INVALID_SOCKET) { assert (!bReadAttemptedAfterClose); bReadAttemptedAfterClose = true; return; } if (bNotifyReadable) { if (EventCallback) (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_NOTIFY_READABLE, NULL, 0); return; } LastIo = gCurrentLoopTime; int total_bytes_read = 0; char readbuffer [16 * 1024 + 1]; for (int i=0; i < 10; i++) { // Don't read just one buffer and then move on. This is faster // if there is a lot of incoming. // But don't read indefinitely. Give other sockets a chance to run. // NOTICE, we're reading one less than the buffer size. // That's so we can put a guard byte at the end of what we send // to user code. int r = recv (sd, readbuffer, sizeof(readbuffer) - 1, 0); //cerr << "<R:" << r << ">"; if (r > 0) { total_bytes_read += r; LastRead = gCurrentLoopTime; // Add a null-terminator at the the end of the buffer // that we will send to the callback. // DO NOT EVER CHANGE THIS. We want to explicitly allow users // to be able to depend on this behavior, so they will have // the option to do some things faster. Additionally it's // a security guard against buffer overflows. readbuffer [r] = 0; _DispatchInboundData (readbuffer, r); } else if (r == 0) { break; } else { // Basically a would-block, meaning we've read everything there is to read. break; } } if (total_bytes_read == 0) { // If we read no data on a socket that selected readable, // it generally means the other end closed the connection gracefully. ScheduleClose (false); //bCloseNow = true; } }
void ConnectionDescriptor::Write() { /* A socket which is in a pending-connect state will select * writable when the disposition of the connect is known. * At that point, check to be sure there are no errors, * and if none, then promote the socket out of the pending * state. * TODO: I haven't figured out how Windows signals errors on * unconnected sockets. Maybe it does the untraditional but * logical thing and makes the socket selectable for error. * If so, it's unsupported here for the time being, and connect * errors will have to be caught by the timeout mechanism. */ if (bConnectPending) { int error; socklen_t len; len = sizeof(error); #ifdef OS_UNIX int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len); #endif #ifdef OS_WIN32 int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len); #endif if ((o == 0) && (error == 0)) { if (EventCallback) (*EventCallback)(GetBinding(), EM_CONNECTION_COMPLETED, "", 0); // 5May09: Moved epoll/kqueue read/write arming into SetConnectPending, so it can be called // from EventMachine_t::AttachFD as well. SetConnectPending (false); } else ScheduleClose (false); //bCloseNow = true; } else { if (bNotifyWritable) { if (EventCallback) (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0); _UpdateEvents(false, true); return; } assert(!bWatchOnly); /* 5May09: Kqueue bugs on OSX cause one extra writable event to fire even though we're using EV_ONESHOT. We ignore this extra event once, but only the first time. If it happens again, we should fall through to the assert(nbytes>0) failure to catch any EM bugs which might cause ::Write to be called in a busy-loop. */ #ifdef HAVE_KQUEUE if (MyEventMachine->UsingKqueue()) { if (OutboundDataSize == 0 && !bGotExtraKqueueEvent) { bGotExtraKqueueEvent = true; return; } else if (OutboundDataSize > 0) { bGotExtraKqueueEvent = false; } } #endif _WriteOutboundData(); } }
void DatagramDescriptor::Read() { int sd = GetSocket(); assert (sd != INVALID_SOCKET); LastIo = gCurrentLoopTime; // This is an extremely large read buffer. // In many cases you wouldn't expect to get any more than 4K. char readbuffer [16 * 1024]; for (int i=0; i < 10; i++) { // Don't read just one buffer and then move on. This is faster // if there is a lot of incoming. // But don't read indefinitely. Give other sockets a chance to run. // NOTICE, we're reading one less than the buffer size. // That's so we can put a guard byte at the end of what we send // to user code. struct sockaddr_in sin; socklen_t slen = sizeof (sin); memset (&sin, 0, slen); int r = recvfrom (sd, readbuffer, sizeof(readbuffer) - 1, 0, (struct sockaddr*)&sin, &slen); //cerr << "<R:" << r << ">"; // In UDP, a zero-length packet is perfectly legal. if (r >= 0) { LastRead = gCurrentLoopTime; // Add a null-terminator at the the end of the buffer // that we will send to the callback. // DO NOT EVER CHANGE THIS. We want to explicitly allow users // to be able to depend on this behavior, so they will have // the option to do some things faster. Additionally it's // a security guard against buffer overflows. readbuffer [r] = 0; // Set up a "temporary" return address so that callers can "reply" to us // from within the callback we are about to invoke. That means that ordinary // calls to "send_data_to_connection" (which is of course misnamed in this // case) will result in packets being sent back to the same place that sent // us this one. // There is a different call (evma_send_datagram) for cases where the caller // actually wants to send a packet somewhere else. memset (&ReturnAddress, 0, sizeof(ReturnAddress)); memcpy (&ReturnAddress, &sin, slen); if (EventCallback) (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r); } else { // Basically a would-block, meaning we've read everything there is to read. break; } } }
void AcceptorDescriptor::Read() { /* Accept up to a certain number of sockets on the listening connection. * Don't try to accept all that are present, because this would allow a DoS attack * in which no data were ever read or written. We should accept more than one, * if available, to keep the partially accepted sockets from backing up in the kernel. */ /* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it * for readability. According to Stevens UNP, it's possible for an acceptor to select readable * and then block when we call accept. For example, the other end resets the connection after * the socket selects readable and before we call accept. The kernel will remove the dead * socket from the accept queue. If the accept queue is now empty, accept will block. */ struct sockaddr_in pin; socklen_t addrlen = sizeof (pin); for (int i=0; i < 10; i++) { int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen); if (sd == INVALID_SOCKET) { // This breaks the loop when we've accepted everything on the kernel queue, // up to 10 new connections. But what if the *first* accept fails? // Does that mean anything serious is happening, beyond the situation // described in the note above? break; } // Set the newly-accepted socket non-blocking. // On Windows, this may fail because, weirdly, Windows inherits the non-blocking // attribute that we applied to the acceptor socket into the accepted one. if (!SetSocketNonblocking (sd)) { //int val = fcntl (sd, F_GETFL, 0); //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) { shutdown (sd, 1); closesocket (sd); continue; } // Disable slow-start (Nagle algorithm). Eventually make this configurable. int one = 1; setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)); ConnectionDescriptor *cd = new ConnectionDescriptor (sd, MyEventMachine); if (!cd) throw std::runtime_error ("no newly accepted connection"); cd->SetServerMode(); if (EventCallback) { (*EventCallback) (GetBinding(), EM_CONNECTION_ACCEPTED, NULL, cd->GetBinding()); } #ifdef HAVE_EPOLL cd->GetEpollEvent()->events = EPOLLIN | (cd->SelectForWrite() ? EPOLLOUT : 0); #endif assert (MyEventMachine); MyEventMachine->Add (cd); #ifdef HAVE_KQUEUE if (cd->SelectForWrite()) MyEventMachine->ArmKqueueWriter (cd); MyEventMachine->ArmKqueueReader (cd); #endif } }
void ConnectionDescriptor::StartTls() { #ifdef WITH_SSL if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding()); _DispatchCiphertext(); #endif #ifdef WITHOUT_SSL throw std::runtime_error ("Encryption not available on this event-machine"); #endif }
void InputManager::HandleControl( Controller& controller ) { //TODO: make this so it's not just cut-and-pasted code iterating over two enums if (controller.GetControllerID() == 0) { int numXboxButtons = sizeof( sBindRecordsOne ) / sizeof(sBindRecordsOne[0] ); for( int i = 0; i < numXboxButtons; i++ ) { XboxButtonBindRecord& rec = sBindRecordsOne[i]; //Is button currently down bool bIsDown = (controller.*rec.CheckFunc)(); bool bWasDown = _xBoxButtonStates[controller.GetControllerID()][rec.HashKey]; //Update key value _xBoxButtonStates[controller.GetControllerID()][rec.HashKey] = bIsDown; InputBinding* pBinding = GetBinding( rec.HashKey ); if( pBinding == NULL ) continue; if( !bWasDown && bIsDown ) { //BUTTON DOWN pBinding->OnKeyDown(); } else if( bWasDown && !bIsDown ) { //BUTTON UP pBinding->OnKeyUp(); } } } else if (controller.GetControllerID() == 1) { int numXboxButtons = sizeof( sBindRecordsTwo ) / sizeof(sBindRecordsTwo[0] ); for( int i = 0; i < numXboxButtons; i++ ) { XboxButtonBindRecord& rec = sBindRecordsTwo[i]; //Is button currently down bool bIsDown = (controller.*rec.CheckFunc)(); bool bWasDown = _xBoxButtonStates[controller.GetControllerID()][rec.HashKey]; //Update key value _xBoxButtonStates[controller.GetControllerID()][rec.HashKey] = bIsDown; InputBinding* pBinding = GetBinding( rec.HashKey ); if( pBinding == NULL ) continue; if( !bWasDown && bIsDown ) { //BUTTON DOWN pBinding->OnKeyDown(); } else if( bWasDown && !bIsDown ) { //BUTTON UP pBinding->OnKeyUp(); } } } else { sysLog.Log("Bad controller ID."); return; } }
HRESULT PendingBreakpoint::BindToModule( Module* mod, Program* prog ) { GuardedArea guard( mBoundBPGuard ); if ( mDeleted ) return E_BP_DELETED; if ( (mState.flags & PBPSF_VIRTUALIZED) == 0 ) return E_FAIL; HRESULT hr = S_OK; BpRequestInfo reqInfo; auto_ptr<BPBinder> binder; hr = MakeBinder( mBPRequest, binder ); if ( FAILED( hr ) ) return hr; // generate bound and error breakpoints BPBinderCallback callback( binder.get(), this, mDocContext.Get(), mDebugger ); callback.BindToModule( mod, prog ); if ( mDocContext.Get() == NULL ) { // set up our document context, since we didn't have one callback.GetDocumentContext( mDocContext ); } ModuleBinding* binding = GetBinding( mod->GetId() ); if ( binding == NULL ) return E_FAIL; // enable all bound BPs if we're enabled if ( mState.state == PBPS_ENABLED ) { ModuleBinding& bind = *binding; for ( ModuleBinding::BPList::iterator itBind = bind.BoundBPs.begin(); itBind != bind.BoundBPs.end(); itBind++ ) { (*itBind)->Enable( TRUE ); } } if ( callback.GetBoundBPCount() > 0 ) { // send a bound event CComPtr<IEnumDebugBoundBreakpoints2> enumBPs; hr = EnumBoundBreakpoints( binding, &enumBPs ); if ( FAILED( hr ) ) return hr; hr = SendBoundEvent( enumBPs ); } else if ( callback.GetErrorBPCount() > 0 ) { // At the beginning, Bind was called, which bound to all mods at the // time. If it sent out a bound BP event, then there can be no error. // If it sent out an error BP event, then there's no need to repeat it. // If you do send out this unneeded event here, then it slows down mod // loading a lot. For ex., with 160 mods, mod loading takes ~10x longer. // So, don't send an error event! hr = S_OK; } else hr = E_FAIL; return hr; }
void PipeDescriptor::Read() { int sd = GetSocket(); if (sd == INVALID_SOCKET) { assert (!bReadAttemptedAfterClose); bReadAttemptedAfterClose = true; return; } LastIo = gCurrentLoopTime; int total_bytes_read = 0; char readbuffer [16 * 1024]; for (int i=0; i < 10; i++) { // Don't read just one buffer and then move on. This is faster // if there is a lot of incoming. // But don't read indefinitely. Give other sockets a chance to run. // NOTICE, we're reading one less than the buffer size. // That's so we can put a guard byte at the end of what we send // to user code. // Use read instead of recv, which on Linux gives a "socket operation // on nonsocket" error. int r = read (sd, readbuffer, sizeof(readbuffer) - 1); //cerr << "<R:" << r << ">"; if (r > 0) { total_bytes_read += r; LastRead = gCurrentLoopTime; // Add a null-terminator at the the end of the buffer // that we will send to the callback. // DO NOT EVER CHANGE THIS. We want to explicitly allow users // to be able to depend on this behavior, so they will have // the option to do some things faster. Additionally it's // a security guard against buffer overflows. readbuffer [r] = 0; if (EventCallback) (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r); } else if (r == 0) { break; } else { // Basically a would-block, meaning we've read everything there is to read. break; } } if (total_bytes_read == 0) { // If we read no data on a socket that selected readable, // it generally means the other end closed the connection gracefully. ScheduleClose (false); //bCloseNow = true; } }