/** * proto_conn_create(s, sas, decr, nopfs, requirepfs, nokeepalive, K, timeo, * callback_dead, cookie): * Create a connection with one end at ${s} and the other end connecting to * the target addresses ${sas}. If ${decr} is 0, encrypt the outgoing data; * if ${decr} is nonzero, decrypt the incoming data. If ${nopfs} is non-zero, * don't use perfect forward secrecy. If ${requirepfs} is non-zero, drop * the connection if the other end tries to disable perfect forward secrecy. * Enable transport layer keep-alives (if applicable) on both sockets if and * only if ${nokeepalive} is zero. Drop the connection if the handshake or * connecting to the target takes more than ${timeo} seconds. When the * connection is dropped, invoke ${callback_dead}(${cookie}). Free ${sas} * once it is no longer needed. Return a cookie which can be passed to * proto_conn_drop. */ void * proto_conn_create(int s, struct sock_addr ** sas, int decr, int nopfs, int requirepfs, int nokeepalive, const struct proto_secret * K, double timeo, int (* callback_dead)(void *), void * cookie) { struct conn_state * C; /* Bake a cookie for this connection. */ if ((C = malloc(sizeof(struct conn_state))) == NULL) goto err0; C->callback_dead = callback_dead; C->cookie = cookie; C->sas = sas; C->decr = decr; C->nopfs = nopfs; C->requirepfs = requirepfs; C->nokeepalive = nokeepalive; C->K = K; C->timeo = timeo; C->s = s; C->t = -1; C->connect_cookie = NULL; C->connect_timeout_cookie = NULL; C->handshake_cookie = NULL; C->handshake_timeout_cookie = NULL; C->k_f = C->k_r = NULL; C->pipe_f = C->pipe_r = NULL; C->stat_f = C->stat_r = 1; /* Start the connect timer. */ if ((C->connect_timeout_cookie = events_timer_register_double( callback_connect_timeout, C, C->timeo)) == NULL) goto err1; /* Connect to target. */ if ((C->connect_cookie = network_connect(C->sas, callback_connect_done, C)) == NULL) goto err2; /* If we're decrypting, start the handshake. */ if (C->decr) { if (starthandshake(C, C->s, C->decr)) goto err3; } /* Success! */ return (C); err3: network_connect_cancel(C->connect_cookie); err2: events_timer_cancel(C->connect_timeout_cookie); err1: free(C); err0: /* Failure! */ return (NULL); }
/* We have connected to the target. */ static int callback_connect_done(void * cookie, int t) { struct conn_state * C = cookie; /* This connection attempt is no longer pending. */ C->connect_cookie = NULL; /* Don't need the target address any more. */ sock_addr_freelist(C->sas); C->sas = NULL; /* We beat the clock. */ events_timer_cancel(C->connect_timeout_cookie); C->connect_timeout_cookie = NULL; /* Did we manage to connect? */ if ((C->t = t) == -1) return (dropconn(C)); /* If we're encrypting, start the handshake. */ if (!C->decr) { if (starthandshake(C, C->t, C->decr)) goto err1; } /* If we have connections and keys, start shuttling data. */ if ((C->t != -1) && (C->k_f != NULL) && (C->k_r != NULL)) { if (launchpipes(C)) goto err1; } /* Success! */ return (0); err1: dropconn(C); /* Failure! */ return (-1); }
/* Handle an incoming connection. */ static int callback_gotconn(void * cookie, int s) { struct accept_state * A = cookie; struct conn_state * C; /* This accept is no longer in progress. */ A->accept_cookie = NULL; /* We have gained a connection. */ A->nconn += 1; /* Bake a cookie for this connection. */ if ((C = malloc(sizeof(struct conn_state))) == NULL) goto err1; C->A = A; C->s = s; C->t = -1; C->connect_cookie = NULL; C->handshake_cookie = NULL; C->connect_timeout_cookie = C->handshake_timeout_cookie = NULL; C->k_f = C->k_r = NULL; C->pipe_f = C->pipe_r = NULL; C->stat_f = C->stat_r = 1; /* Start the connect timer. */ if ((C->connect_timeout_cookie = events_timer_register_double( callback_connect_timeout, C, C->A->timeo)) == NULL) goto err2; /* Connect to target. */ if ((C->connect_cookie = network_connect(A->sas, callback_connect_done, C)) == NULL) goto err3; /* If we're decrypting, start the handshake. */ if (C->A->decr) { if (starthandshake(C, C->s, C->A->decr)) goto err4; } /* Accept another connection if we can. */ if (doaccept(A)) goto err0; /* Success! */ return (0); err4: network_connect_cancel(C->connect_cookie); err3: events_timer_cancel(C->connect_timeout_cookie); err2: free(C); err1: A->nconn -= 1; close(s); err0: /* Failure! */ return (-1); }