Ejemplo n.º 1
0
// 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;
}
Ejemplo n.º 2
0
// 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;
}
Ejemplo n.º 3
0
// 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;
}