void pkr_relay_incoming(pk_lua_t* LUA, int result, void* void_data) { struct incoming_conn_state* ics = (struct incoming_conn_state*) void_data; PK_TRACE_FUNCTION; if (ics->parse_state == PARSE_FAILED) { pk_log(PK_LOG_TUNNEL_DATA|PK_LOG_ERROR, "pkr_relay_incoming() invoked for dead ics"); return; /* Should never happen */ } if (result == PARSE_WANT_MORE_DATA) { if (time(NULL) - ics->created > 5) { /* We don't wait forever for more data; that would make resource * exhaustion DOS attacks against the server trivially easy. */ _pkr_close(ics, 0, "Timed out"); } else { /* Slow down a tiny bit, unless there are jobs in the queue waiting * for us to finish. */ if (ics->pkm->blocking_jobs.count < 1) sleep_ms(100); _pkr_process_readable(ics); } } else { /* If we get this far, then the request has been parsed and we have * in ics details about what needs to be done. So we do it! :) */ _pkr_close(ics, 0, "FIXME"); } }
int pkr_conn_accepted_cb(int sockfd, void* void_pkm) { struct incoming_conn_state* ics; struct pk_manager* pkm = (struct pk_manager*) void_pkm; struct pk_backend_conn* pkb; socklen_t slen; char rbuf[128]; char lbuf[128]; PK_TRACE_FUNCTION; ics = malloc(sizeof(struct incoming_conn_state)); ics->pkm = pkm; ics->pkb = NULL; ics->tunnel = NULL; ics->hostname = NULL; ics->parsed_as = PROTO_UNKNOWN; ics->parse_state = PARSE_UNDECIDED; ics->created = pk_time(); ics->unparsed_data = NULL; slen = sizeof(ics->local_addr); getsockname(sockfd, (struct sockaddr*) &(ics->local_addr), &slen); slen = sizeof(ics->remote_addr); getpeername(sockfd, (struct sockaddr*) &(ics->remote_addr), &slen); pk_log(PK_LOG_TUNNEL_DATA, "Accepted; remote=%s; local=%s; fd=%d", in_addr_to_str((struct sockaddr*) &(ics->remote_addr), rbuf, 128), in_addr_to_str((struct sockaddr*) &(ics->local_addr), lbuf, 128), sockfd); /* Allocate a connection for this request or die... */ sprintf(lbuf, "!NEW:%d", ics->remote_addr.sin_port); if (NULL == (pkb = pkm_alloc_be_conn(pkm, NULL, lbuf))) { _pkr_close(ics, PK_LOG_ERROR, "BE alloc failed"); PKS_close(sockfd); return -1; } pkb->kite = NULL; pkb->conn.sockfd = sockfd; ics->pkb = pkb; set_non_blocking(sockfd); ev_io_init(&(pkb->conn.watch_r), pkr_new_conn_readable_cb, PKS_EV_FD(sockfd), EV_READ); pkb->conn.watch_r.data = (void *) ics; ev_io_start(pkm->loop, &(pkb->conn.watch_r)); return 0; }
void pkr_relay_incoming(int result, void* void_data) { struct incoming_conn_state* ics = (struct incoming_conn_state*) void_data; PK_TRACE_FUNCTION; if (ics->parse_state == PARSE_FAILED) { pk_log(PK_LOG_TUNNEL_DATA|PK_LOG_ERROR, "pkr_relay_incoming() invoked for dead ics"); return; /* Should never happen */ } if (result == PARSE_WANT_MORE_DATA) { if (pk_time() - ics->created > 5) { /* We don't wait forever for more data; that would make resource * exhaustion DOS attacks against the server trivially easy. */ _pkr_close(ics, 0, "Timed out"); } else { /* Slow down a tiny bit, unless there are jobs in the queue waiting * for us to finish. */ if (ics->pkm->blocking_jobs.count < 1) sleep_ms(100); _pkr_process_readable(ics); } } else { /* If we get this far, then the request has been parsed and we have * in ics details about what needs to be done. So we do it! :) */ if (ics->parsed_as == PROTO_PAGEKITE) { char* response = NULL; struct pke_event* ev = pke_post_blocking_event(NULL, PK_EV_TUNNEL_REQUEST, 0, ics->unparsed_data, NULL, &response); int rlen = 0; if (response && *response) { rlen = strlen(response); pkc_write(&(ics->pkb->conn), response, rlen); } int result_ok = rlen && ev && (ev->response_code & PK_EV_RESPOND_TRUE); if (result_ok) { /* FIXME: Upgrade this connection to a tunnel */ int flying = 0; struct pk_kite_request* pkr = pk_parse_pagekite_response( response, rlen + 1, NULL, NULL); if (pkr != NULL) { struct pk_kite_request* p; for (p = pkr; p->status != PK_KITE_UNKNOWN; p++) { if (p->status == PK_KITE_FLYING) { /* FIXME: Record kite->tunnel mapping */ pk_log(PK_LOG_MANAGER_DEBUG, "Accepted kite: %s://%s:%d", p->kite->protocol, p->kite->public_domain, p->kite->public_port); flying++; } else { pk_log(PK_LOG_MANAGER_DEBUG, "Rejected kite: %s://%s:%d (%d)", p->kite->protocol, p->kite->public_domain, p->kite->public_port, p->status); } } free(pkr); /* FIXME: Add to event loop */ } if (!flying) result_ok = 0; } if (ev) pke_free_event(NULL, ev->event_code); if (response) free(response); if (!result_ok) _pkr_close(ics, 0, "Rejected"); } else { _pkr_close(ics, 0, "FIXME"); } } }
static int _pkr_process_readable(struct incoming_conn_state* ics) { char peeked[PEEK_BYTES+1]; int bytes; int result = PARSE_UNDECIDED; PK_TRACE_FUNCTION; /* We have data! Peek at it to find out what it is... */ bytes = PKS_peek(ics->pkb->conn.sockfd, peeked, PEEK_BYTES); if (bytes == 0) { /* Connection closed already */ result = PARSE_FAILED; } else if (bytes < 0) { pk_log(PK_LOG_MANAGER_DEBUG, "_pkr_process_readable(%d): peek failed %d=%s!", ics->pkb->conn.sockfd, errno, strerror(errno)); if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) { result = PARSE_WANT_MORE_DATA; } else { result = PARSE_FAILED; } } else { peeked[bytes] = '\0'; } if (!result) result = _pkr_parse_ssltls(peeked, bytes, ics); if (!result) result = _pkr_parse_http(peeked, bytes, ics); /* No result yet? Do we have enough data? */ if (!result) { if (bytes > 4) { result = PARSE_FAILED; } else { result = PARSE_WANT_MORE_DATA; } } ics->parse_state = result; pk_log(PK_LOG_MANAGER_DEBUG, "_pkr_process_readable(%d): result=%d, bytes=%d", ics->pkb->conn.sockfd, result, bytes); if ((result == PARSE_WANT_MORE_DATA) || (result == PARSE_MATCH_SLOW)) { /* Parsers found something or we still need more data. * Punt to another thread for further processing. */ pkb_add_job(&(ics->pkm->blocking_jobs), PK_RELAY_INCOMING, result, ics); } else if (result == PARSE_MATCH_FAST) { /* Parsers found something. Process now. */ pkr_relay_incoming(result, ics); } else { /* result == PARSE_FAILED */ _pkr_close(ics, 0, "No tunnel found"); } PK_CHECK_MEMORY_CANARIES; return result; }