int BIO_get_host_ip(const char *str, unsigned char *ip) { BIO_ADDRINFO *res = NULL; int ret = 0; if (BIO_sock_init() != 1) return 0; /* don't generate another error code here */ if (BIO_lookup(str, NULL, BIO_LOOKUP_CLIENT, AF_INET, SOCK_STREAM, &res)) { size_t l; if (BIO_ADDRINFO_family(res) != AF_INET) { BIOerr(BIO_F_BIO_GET_HOST_IP, BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET); } else { BIO_ADDR_rawaddress(BIO_ADDRINFO_address(res), NULL, &l); /* Because only AF_INET addresses will reach this far, we can assert that l should be 4 */ OPENSSL_assert(l == 4); BIO_ADDR_rawaddress(BIO_ADDRINFO_address(res), ip, &l); ret = 1; } BIO_ADDRINFO_free(res); } else { ERR_add_error_data(2, "host=", str); } return ret; }
int BIO_get_accept_socket(char *host, int bind_mode) { int s = INVALID_SOCKET; char *h = NULL, *p = NULL; BIO_ADDRINFO *res = NULL; if (!BIO_parse_hostserv(host, &h, &p, BIO_PARSE_PRIO_SERV)) return INVALID_SOCKET; if (BIO_sock_init() != 1) return INVALID_SOCKET; if (BIO_lookup(h, p, BIO_LOOKUP_SERVER, AF_UNSPEC, SOCK_STREAM, &res) != 0) goto err; if ((s = BIO_socket(BIO_ADDRINFO_family(res), BIO_ADDRINFO_socktype(res), BIO_ADDRINFO_protocol(res), 0)) == INVALID_SOCKET) { s = INVALID_SOCKET; goto err; } if (!BIO_listen(s, BIO_ADDRINFO_address(res), bind_mode ? BIO_SOCK_REUSEADDR : 0)) { BIO_closesocket(s); s = INVALID_SOCKET; } err: BIO_ADDRINFO_free(res); OPENSSL_free(h); OPENSSL_free(p); return s; }
int BIO_get_port(const char *str, unsigned short *port_ptr) { BIO_ADDRINFO *res = NULL; int ret = 0; if (str == NULL) { BIOerr(BIO_F_BIO_GET_PORT, BIO_R_NO_PORT_DEFINED); return (0); } if (BIO_sock_init() != 1) return 0; /* don't generate another error code here */ if (BIO_lookup(NULL, str, BIO_LOOKUP_CLIENT, AF_INET, SOCK_STREAM, &res)) { if (BIO_ADDRINFO_family(res) != AF_INET) { BIOerr(BIO_F_BIO_GET_PORT, BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET); } else { *port_ptr = ntohs(BIO_ADDR_rawport(BIO_ADDRINFO_address(res))); ret = 1; } BIO_ADDRINFO_free(res); } else { ERR_add_error_data(2, "host=", str); } return ret; }
static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) { int *ip; long ret = 1; BIO_ACCEPT *data; char **pp; data = (BIO_ACCEPT *)b->ptr; switch (cmd) { case BIO_CTRL_RESET: ret = 0; data->state = ACPT_S_BEFORE; acpt_close_socket(b); BIO_ADDRINFO_free(data->addr_first); data->addr_first = NULL; b->flags = 0; break; case BIO_C_DO_STATE_MACHINE: /* use this one to start the connection */ ret = (long)acpt_state(b, data); break; case BIO_C_SET_ACCEPT: if (ptr != NULL) { if (num == 0) { char *hold_serv = data->param_serv; /* We affect the hostname regardless. However, the input * string might contain a host:service spec, so we must * parse it, which might or might not affect the service */ OPENSSL_free(data->param_addr); data->param_addr = NULL; ret = BIO_parse_hostserv(ptr, &data->param_addr, &data->param_serv, BIO_PARSE_PRIO_SERV); if (hold_serv != data->param_serv) OPENSSL_free(hold_serv); b->init = 1; } else if (num == 1) { OPENSSL_free(data->param_serv); data->param_serv = BUF_strdup(ptr); b->init = 1; } else if (num == 2) { data->bind_mode |= BIO_SOCK_NONBLOCK; } else if (num == 3) { BIO_free(data->bio_chain); data->bio_chain = (BIO *)ptr; } else if (num == 4) { data->accept_family = *(int *)ptr; } } else { if (num == 2) { data->bind_mode &= ~BIO_SOCK_NONBLOCK; } } break; case BIO_C_SET_NBIO: if (num != 0) data->accepted_mode |= BIO_SOCK_NONBLOCK; else data->accepted_mode &= ~BIO_SOCK_NONBLOCK; break; case BIO_C_SET_FD: b->init = 1; b->num = *((int *)ptr); data->accept_sock = b->num; data->state = ACPT_S_ACCEPT; b->shutdown = (int)num; b->init = 1; break; case BIO_C_GET_FD: if (b->init) { ip = (int *)ptr; if (ip != NULL) *ip = data->accept_sock; ret = data->accept_sock; } else ret = -1; break; case BIO_C_GET_ACCEPT: if (b->init) { if (num == 0 && ptr != NULL) { pp = (char **)ptr; *pp = data->cache_accepting_name; } else if (num == 1 && ptr != NULL) { pp = (char **)ptr; *pp = data->cache_accepting_serv; } else if (num == 2 && ptr != NULL) { pp = (char **)ptr; *pp = data->cache_peer_name; } else if (num == 3 && ptr != NULL) { pp = (char **)ptr; *pp = data->cache_peer_serv; } else if (num == 4) { switch (BIO_ADDRINFO_family(data->addr_iter)) { #ifdef AF_INET6 case AF_INET6: ret = BIO_FAMILY_IPV6; break; #endif case AF_INET: ret = BIO_FAMILY_IPV4; break; case 0: ret = data->accept_family; break; default: ret = -1; break; } } else ret = -1; } else ret = -1; break; case BIO_CTRL_GET_CLOSE: ret = b->shutdown; break; case BIO_CTRL_SET_CLOSE: b->shutdown = (int)num; break; case BIO_CTRL_PENDING: case BIO_CTRL_WPENDING: ret = 0; break; case BIO_CTRL_FLUSH: break; case BIO_C_SET_BIND_MODE: data->bind_mode = (int)num; break; case BIO_C_GET_BIND_MODE: ret = (long)data->bind_mode; break; case BIO_CTRL_DUP: /*- dbio=(BIO *)ptr; if (data->param_port) EAY EAY BIO_set_port(dbio,data->param_port); if (data->param_hostname) BIO_set_hostname(dbio,data->param_hostname); BIO_set_nbio(dbio,data->nbio); */ break; default: ret = 0; break; } 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; }