// Set error and report it back immediately: static void connections_E(struct connection* c, int (*errorcallback)(struct connection* c, int error), int error) { if (!c->errorreported) { c->errorreported = 1; c->error = error; if (errorcallback) { if (!errorcallback(c, error)) { // the error callback failed. // however, there is nothing we could do about it! return; } } } return; }
// Set error and report it back immediately: static void connections_E(struct connection* c, int (*errorcallback)(struct connection* c, int error), int error) { #ifdef CONNECTIONSDEBUG printinfo("[connections] connections_E triggered."); #endif if (!c->errorreported) { c->errorreported = 1; c->error = error; if (errorcallback) { if (!errorcallback(c, error)) { // the error callback failed. // however, there is nothing we could do about it! return; } } } return; }
// Check all connections for events, updates etc int connections_CheckAll(int (*connectedcallback)(struct connection* c), int (*readcallback)(struct connection* c, char* data, unsigned int datalength), int (*errorcallback)(struct connection* c, int error)) { // initialise socket system (just in case it's not done yet): if (!so_Startup()) { return 1; } struct connection* c = connectionlist; while (c) { struct connection* cnext = c->next; // don't process connections with errors if (c->error >= 0) { if (!c->errorreported) { c->errorreported = 1; if (errorcallback) { if (!errorcallback(c, c->error)) { // an error occured. proceed to next connect: c = cnext; continue; } } } } // check for auto close: if (c->canautoclose && c->wantautoclose && (c->error >= 0 || c->lastreadtime + 30000 < time_getMilliseconds()) && !c->closewhensent) { if (c->error >= 0) { // connection already error'd, we can get rid of it: #ifdef CONNECTIONSDEBUG printinfo("[connections] autoclosing connection %d", c->socket); #endif c->autoclosecallback(c); connections_Close(c); c = cnext; } else { // make the connection error: connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONAUTOCLOSE); } continue; } // check for close after send: if (c->outbufbytes <= 0 && c->closewhensent) { if (c->luarefcount <= 0) { #ifdef CONNECTIONSDEBUG printinfo("[connections] closing connection %d " "since data is sent", c->socket); #endif connections_Close(c); } else { // lua still has a reference, don't close } c = cnext; continue; } // check host resolve requests first: if (c->error < 0 && !c->closewhensent && (c->hostresolveptr || c->hostresolveptrv6)) { int rqstate1 = RESOLVESTATUS_SUCCESS; if (c->hostresolveptr) { rqstate1 = hostresolv_GetRequestStatus(c->hostresolveptr); } int rqstate2 = RESOLVESTATUS_SUCCESS; if (c->hostresolveptrv6) { rqstate2 = hostresolv_GetRequestStatus(c->hostresolveptrv6); } if (rqstate1 != RESOLVESTATUS_PENDING && rqstate2 != RESOLVESTATUS_PENDING) { // requests are done, get ips: char* ipv4 = NULL; char* ipv6 = NULL; // ipv4 ip: const char* p = NULL; if (rqstate1 == RESOLVESTATUS_SUCCESS && c->hostresolveptr) { p = hostresolv_GetRequestResult(c->hostresolveptr); } if (p) { ipv4 = strdup(p); } // ipv6 ip: p = NULL; if (rqstate2 == RESOLVESTATUS_SUCCESS && c->hostresolveptrv6) { p = hostresolv_GetRequestResult(c->hostresolveptrv6); } if (p) { ipv6 = strdup(p); } // free requests: if (c->hostresolveptr) { hostresolv_CancelRequest(c->hostresolveptr); } if (c->hostresolveptrv6) { hostresolv_CancelRequest(c->hostresolveptrv6); } c->hostresolveptr = NULL; c->hostresolveptrv6 = NULL; #ifdef CONNECTIONSDEBUG printinfo("[connections] resolved host, results: v4: %s, v6: %s", ipv4, ipv6); #endif // if we got a v6 ip, connect there first: int result; if (ipv6) { // Prefer an IPv6 connection: result = connections_TryConnect(c, ipv6); free(ipv6); if (!result) { if (ipv4) { c->error = -1; // Try an IPv4 connection: result = connections_TryConnect(c, ipv4); free(ipv4); if (!result) { connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONFAILED); } } c = cnext; continue; } // ok we are about to connect, preserve v4 ip for later trying in case it fails: c->retryv4ip = ipv4; // then continue: c = cnext; continue; } else { if (ipv4) { // Attempt an IPv4 connection: result = connections_TryConnect(c, ipv4); if (result) { free(ipv4); c = cnext; continue; } // Didn't work, so nothing we can do: free(ipv4); connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONFAILED); } else { connections_E(c, errorcallback, CONNECTIONERROR_NOSUCHHOST); } c = cnext; continue; } } } // if the connection is attempting to connect, check if it succeeded: if (c->error < 0 && !c->closewhensent && !c->connected && c->socket >= 0) { if (so_SelectSaysWrite(c->socket, &c->sslptr)) { if (c->outbufbytes <= 0) { so_SelectWantWrite(c->socket, 0); } if (so_CheckIfConnected(c->socket, &c->sslptr)) { #ifdef CONNECTIONSDEBUG printinfo("[connections] now connected"); #endif c->connected = 1; if (connectedcallback) { if (!connectedcallback(c)) { // a callback error occured. // we will simply continue. } } } else { // we aren't connected! if (c->retryv4ip) { // we tried ipv6, now try ipv4 #ifdef CONNECTIONSDEBUG printinfo("[connections] retrying v4 after v6 " "fail..."); #endif // attempt to connect: int result = connections_TryConnect(c, c->retryv4ip); free(c->retryv4ip); c->retryv4ip = NULL; if (!result) { connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONFAILED); } c = cnext; continue; } #ifdef CONNECTIONSDEBUG printinfo("[connections] connection couldn't be " "established"); #endif connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONFAILED); c = cnext; continue; } } } // read things if we can: if (c->error < 0 && c->socket >= 0 && !c->closewhensent && c->connected) { if (so_SelectSaysRead(c->socket, &c->sslptr)) { if (c->inbufbytes >= c->inbufsize && c->linebuffered == 1) { // we will break this mega line into two: // first, send without line processing: c->linebuffered = 0; int closed = 0; if (!connections_ProcessReceivedData(c, readcallback, &closed)) { // an error occured in te read callback. // we will simply continue. } if (closed) { // connection was closed by callback c = cnext; continue; } if (c->error >= 0) { // lua closed this connection c = cnext; continue; } // then, turn it back on: c->linebuffered = 1; } // read new bytes into the buffer: int r = so_ReceiveSSLData(c->socket, c->inbuf + c->inbufbytes, c->inbufsize - c->inbufbytes, &c->sslptr); if (r == 0) { // connection closed. send out all data we still have: if (c->inbufbytes > 0) { int closed = 0; if (!connections_ProcessReceivedData( c, readcallback, &closed)) { // an error occured in the read callback. // we will simply continue. } if (closed) { // connection was closed by callback c = cnext; continue; } } // then error: connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONCLOSED); #ifdef CONNECTIONSDEBUG printinfo("[connections] receive on %d returned " "end of stream", c->socket); #endif c = cnext; continue; } if (r > 0) { // we successfully received new bytes c->inbufbytes += r; c->lastreadtime = time_getMilliseconds(); int closed = 0; if (!connections_ProcessReceivedData(c, readcallback, &closed)) { // an error occured in the read callback. // we will simply continue. } if (closed) { // connection was closed by callback c = cnext; continue; } } } } // write things if we can: if ((c->error < 0 || (c->closewhensent)) && c->connected && c->outbufbytes > 0 && c->socket >= 0) { if (so_SelectSaysWrite(c->socket, &c->sslptr)) { int r = so_SendSSLData(c->socket, c->outbuf + c->outbufoffset, c->outbufbytes, &c->sslptr); if (r == 0) { // connection closed: connections_E(c, errorcallback, CONNECTIONERROR_CONNECTIONCLOSED); #ifdef CONNECTIONSDEBUG printinfo("[connections] send returned end of stream"); #endif c = cnext; continue; } // remove sent bytes from buffer: if (r > 0) { c->outbufbytes -= r; c->outbufoffset += r; so_SelectWantWrite(c->socket, 1); if (c->outbufbytes <= 0) { c->outbufoffset = 0; so_SelectWantWrite(c->socket, 0); } else { if (c->outbufoffset > CONNECTIONOUTBUFSIZE/2) { // move buffer contents back to beginning memmove(c->outbuf, c->outbuf + c->outbufoffset, c->outbufbytes); c->outbufoffset = 0; } } } } } // check for close after send: if (c->outbufbytes <= 0 && c->closewhensent) { if (c->luarefcount <= 0) { #ifdef CONNECTIONSDEBUG printinfo("[connections] closing connection %d since data is sent", c->socket); #endif connections_Close(c); } else { // lua still has a reference, but close at least the socket: so_CloseSSLSocket(c->socket, &c->sslptr); c->socket = -1; } c = cnext; continue; } c = cnext; } return 1; }