int BIO_accept(int sock, char **ip_port) { BIO_ADDR res; int ret = -1; ret = BIO_accept_ex(sock, &res, 0); if (ret == (int)INVALID_SOCKET) { if (BIO_sock_should_retry(ret)) { ret = -2; goto end; } SYSerr(SYS_F_ACCEPT, get_last_socket_error()); BIOerr(BIO_F_BIO_ACCEPT, BIO_R_ACCEPT_ERROR); goto end; } if (ip_port != NULL) { char *host = BIO_ADDR_hostname_string(&res, 1); char *port = BIO_ADDR_service_string(&res, 1); *ip_port = OPENSSL_zalloc(strlen(host) + strlen(port) + 2); strcpy(*ip_port, host); strcat(*ip_port, ":"); strcat(*ip_port, port); OPENSSL_free(host); OPENSSL_free(port); } end: return ret; }
static int acpt_state(BIO *b, BIO_ACCEPT *c) { BIO *bio = NULL, *dbio; int s = -1, ret = -1; for (;;) { switch (c->state) { case ACPT_S_BEFORE: if (c->param_addr == NULL && c->param_serv == NULL) { BIOerr(BIO_F_ACPT_STATE, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED); ERR_add_error_data(4, "hostname=", c->param_addr, " service=", c->param_serv); goto exit_loop; } /* Because we're starting a new bind, any cached name and serv * are now obsolete and need to be cleaned out. * QUESTION: should this be done in acpt_close_socket() instead? */ OPENSSL_free(c->cache_accepting_name); c->cache_accepting_name = NULL; OPENSSL_free(c->cache_accepting_serv); c->cache_accepting_serv = NULL; OPENSSL_free(c->cache_peer_name); c->cache_peer_name = NULL; OPENSSL_free(c->cache_peer_serv); c->cache_peer_serv = NULL; c->state = ACPT_S_GET_ADDR; break; case ACPT_S_GET_ADDR: { int family = AF_UNSPEC; switch (c->accept_family) { case BIO_FAMILY_IPV6: if (1) { /* This is a trick we use to avoid bit rot. * at least the "else" part will always be * compiled. */ #ifdef AF_INET6 family = AF_INET6; } else { #endif BIOerr(BIO_F_ACPT_STATE, BIO_R_UNAVAILABLE_IP_FAMILY); goto exit_loop; } break; case BIO_FAMILY_IPV4: family = AF_INET; break; case BIO_FAMILY_IPANY: family = AF_UNSPEC; break; default: BIOerr(BIO_F_ACPT_STATE, BIO_R_UNSUPPORTED_IP_FAMILY); goto exit_loop; } if (BIO_lookup(c->param_addr, c->param_serv, BIO_LOOKUP_SERVER, family, SOCK_STREAM, &c->addr_first) == 0) goto exit_loop; } if (c->addr_first == NULL) { BIOerr(BIO_F_ACPT_STATE, BIO_R_LOOKUP_RETURNED_NOTHING); goto exit_loop; } /* We're currently not iterating, but set this as preparation * for possible future development in that regard */ c->addr_iter = c->addr_first; c->state = ACPT_S_CREATE_SOCKET; break; case ACPT_S_CREATE_SOCKET: ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter), BIO_ADDRINFO_socktype(c->addr_iter), BIO_ADDRINFO_protocol(c->addr_iter), 0); if (ret == (int)INVALID_SOCKET) { SYSerr(SYS_F_SOCKET, get_last_socket_error()); ERR_add_error_data(4, "hostname=", c->param_addr, " service=", c->param_serv); BIOerr(BIO_F_ACPT_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET); goto exit_loop; } c->accept_sock = ret; b->num = ret; c->state = ACPT_S_LISTEN; break; case ACPT_S_LISTEN: { if (!BIO_listen(c->accept_sock, BIO_ADDRINFO_address(c->addr_iter), c->bind_mode)) { BIO_closesocket(c->accept_sock); goto exit_loop; } } { union BIO_sock_info_u info; info.addr = &c->cache_accepting_addr; if (!BIO_sock_info(c->accept_sock, BIO_SOCK_INFO_ADDRESS, &info)) { BIO_closesocket(c->accept_sock); goto exit_loop; } } c->cache_accepting_name = BIO_ADDR_hostname_string(&c->cache_accepting_addr, 1); c->cache_accepting_serv = BIO_ADDR_service_string(&c->cache_accepting_addr, 1); c->state = ACPT_S_ACCEPT; s = -1; ret = 1; goto end; case ACPT_S_ACCEPT: if (b->next_bio != NULL) { c->state = ACPT_S_OK; break; } BIO_clear_retry_flags(b); b->retry_reason = 0; OPENSSL_free(c->cache_peer_name); c->cache_peer_name = NULL; OPENSSL_free(c->cache_peer_serv); c->cache_peer_serv = NULL; s = BIO_accept_ex(c->accept_sock, &c->cache_peer_addr, c->accepted_mode); /* If the returned socket is invalid, this might still be * retryable */ if (s < 0) { if (BIO_sock_should_retry(s)) { BIO_set_retry_special(b); b->retry_reason = BIO_RR_ACCEPT; goto end; } } /* If it wasn't retryable, we fail */ if (s < 0) { ret = s; goto exit_loop; } bio = BIO_new_socket(s, BIO_CLOSE); if (bio == NULL) goto exit_loop; BIO_set_callback(bio, BIO_get_callback(b)); BIO_set_callback_arg(bio, BIO_get_callback_arg(b)); /* * If the accept BIO has an bio_chain, we dup it and put the new * socket at the end. */ if (c->bio_chain != NULL) { if ((dbio = BIO_dup_chain(c->bio_chain)) == NULL) goto exit_loop; if (!BIO_push(dbio, bio)) goto exit_loop; bio = dbio; } if (BIO_push(b, bio) == NULL) goto exit_loop; c->cache_peer_name = BIO_ADDR_hostname_string(&c->cache_peer_addr, 1); c->cache_peer_serv = BIO_ADDR_service_string(&c->cache_peer_addr, 1); c->state = ACPT_S_OK; bio = NULL; ret = 1; goto end; case ACPT_S_OK: if (b->next_bio == NULL) { c->state = ACPT_S_ACCEPT; break; } ret = 1; goto end; default: ret = 0; goto end; } } exit_loop: if (bio != NULL) BIO_free(bio); else if (s >= 0) BIO_closesocket(s); end: return ret; }