Add BIO_bind function to bind local address for a socket.
Add -bind option to s_client application to allow specification of local address for connection. Reviewed-by: Andy Polyakov <appro@openssl.org> Reviewed-by: Rich Salz <rsalz@openssl.org> (Merged from https://github.com/openssl/openssl/pull/5272)
This commit is contained in:
parent
b383aa2081
commit
ebc0168384
11 changed files with 145 additions and 26 deletions
|
@ -38,6 +38,7 @@ int ssl_print_groups(BIO *out, SSL *s, int noshared);
|
|||
#endif
|
||||
int ssl_print_tmp_key(BIO *out, SSL *s);
|
||||
int init_client(int *sock, const char *host, const char *port,
|
||||
const char *bindhost, const char *bindport,
|
||||
int family, int type, int protocol);
|
||||
int should_retry(int i);
|
||||
|
||||
|
|
|
@ -570,7 +570,7 @@ static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset)
|
|||
|
||||
typedef enum OPTION_choice {
|
||||
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
|
||||
OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_UNIX,
|
||||
OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_BIND, OPT_UNIX,
|
||||
OPT_XMPPHOST, OPT_VERIFY, OPT_NAMEOPT,
|
||||
OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN,
|
||||
OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET,
|
||||
|
@ -612,6 +612,7 @@ const OPTIONS s_client_options[] = {
|
|||
{"port", OPT_PORT, 'p', "Use -connect instead"},
|
||||
{"connect", OPT_CONNECT, 's',
|
||||
"TCP/IP where to connect (default is :" PORT ")"},
|
||||
{"bind", OPT_BIND, 's', "bind local address for connection"},
|
||||
{"proxy", OPT_PROXY, 's',
|
||||
"Connect to via specified proxy to the real server"},
|
||||
#ifdef AF_UNIX
|
||||
|
@ -884,10 +885,11 @@ int s_client_main(int argc, char **argv)
|
|||
const SSL_METHOD *meth = TLS_client_method();
|
||||
const char *CApath = NULL, *CAfile = NULL;
|
||||
char *cbuf = NULL, *sbuf = NULL;
|
||||
char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL;
|
||||
char *mbuf = NULL, *proxystr = NULL, *connectstr = NULL, *bindstr = NULL;
|
||||
char *cert_file = NULL, *key_file = NULL, *chain_file = NULL;
|
||||
char *chCApath = NULL, *chCAfile = NULL, *host = NULL;
|
||||
char *port = OPENSSL_strdup(PORT);
|
||||
char *bindhost = NULL, *bindport = NULL;
|
||||
char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL;
|
||||
char *ReqCAfile = NULL;
|
||||
char *sess_in = NULL, *crl_file = NULL, *p;
|
||||
|
@ -1053,6 +1055,9 @@ int s_client_main(int argc, char **argv)
|
|||
connect_type = use_inet;
|
||||
freeandcopy(&connectstr, opt_arg());
|
||||
break;
|
||||
case OPT_BIND:
|
||||
freeandcopy(&bindstr, opt_arg());
|
||||
break;
|
||||
case OPT_PROXY:
|
||||
proxystr = opt_arg();
|
||||
starttls_proto = PROTO_CONNECT;
|
||||
|
@ -1554,6 +1559,18 @@ int s_client_main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (bindstr != NULL) {
|
||||
int res;
|
||||
res = BIO_parse_hostserv(bindstr, &bindhost, &bindport,
|
||||
BIO_PARSE_PRIO_HOST);
|
||||
if (!res) {
|
||||
BIO_printf(bio_err,
|
||||
"%s: -bind argument parameter malformed or ambiguous\n",
|
||||
prog);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AF_UNIX
|
||||
if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) {
|
||||
BIO_printf(bio_err,
|
||||
|
@ -1976,8 +1993,8 @@ int s_client_main(int argc, char **argv)
|
|||
}
|
||||
|
||||
re_start:
|
||||
if (init_client(&s, host, port, socket_family, socket_type, protocol)
|
||||
== 0) {
|
||||
if (init_client(&s, host, port, bindhost, bindport, socket_family,
|
||||
socket_type, protocol) == 0) {
|
||||
BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error());
|
||||
BIO_closesocket(s);
|
||||
goto end;
|
||||
|
@ -3082,6 +3099,7 @@ int s_client_main(int argc, char **argv)
|
|||
OPENSSL_free(srp_arg.srppassin);
|
||||
#endif
|
||||
OPENSSL_free(connectstr);
|
||||
OPENSSL_free(bindstr);
|
||||
OPENSSL_free(host);
|
||||
OPENSSL_free(port);
|
||||
X509_VERIFY_PARAM_free(vpm);
|
||||
|
|
|
@ -43,6 +43,8 @@ BIO_ADDR *ourpeer = NULL;
|
|||
* @sock: pointer to storage of resulting socket.
|
||||
* @host: the host name or path (for AF_UNIX) to connect to.
|
||||
* @port: the port to connect to (ignored for AF_UNIX).
|
||||
* @bindhost: source host or path (for AF_UNIX).
|
||||
* @bindport: source port (ignored for AF_UNIX).
|
||||
* @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
|
||||
* AF_UNSPEC
|
||||
* @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
|
||||
|
@ -58,10 +60,14 @@ BIO_ADDR *ourpeer = NULL;
|
|||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
int init_client(int *sock, const char *host, const char *port,
|
||||
const char *bindhost, const char *bindport,
|
||||
int family, int type, int protocol)
|
||||
{
|
||||
BIO_ADDRINFO *res = NULL;
|
||||
BIO_ADDRINFO *bindaddr = NULL;
|
||||
const BIO_ADDRINFO *ai = NULL;
|
||||
const BIO_ADDRINFO *bi = NULL;
|
||||
int found = 0;
|
||||
int ret;
|
||||
|
||||
if (BIO_sock_init() != 1)
|
||||
|
@ -74,6 +80,15 @@ int init_client(int *sock, const char *host, const char *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (bindhost != NULL || bindport != NULL) {
|
||||
ret = BIO_lookup_ex(bindhost, bindport, BIO_LOOKUP_CLIENT,
|
||||
family, type, protocol, &bindaddr);
|
||||
if (ret == 0) {
|
||||
ERR_print_errors (bio_err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
|
||||
/* Admittedly, these checks are quite paranoid, we should not get
|
||||
|
@ -85,6 +100,16 @@ int init_client(int *sock, const char *host, const char *port,
|
|||
&& (protocol == 0
|
||||
|| protocol == BIO_ADDRINFO_protocol(ai)));
|
||||
|
||||
if (bindaddr != NULL) {
|
||||
for (bi = bindaddr; bi != NULL; bi = BIO_ADDRINFO_next(bi)) {
|
||||
if (BIO_ADDRINFO_family(bi) == BIO_ADDRINFO_family(ai))
|
||||
break;
|
||||
}
|
||||
if (bi == NULL)
|
||||
continue;
|
||||
++found;
|
||||
}
|
||||
|
||||
*sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai),
|
||||
BIO_ADDRINFO_protocol(ai), 0);
|
||||
if (*sock == INVALID_SOCKET) {
|
||||
|
@ -94,6 +119,15 @@ int init_client(int *sock, const char *host, const char *port,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (bi != NULL) {
|
||||
if (!BIO_bind(*sock, BIO_ADDRINFO_address(bi),
|
||||
BIO_SOCK_REUSEADDR)) {
|
||||
BIO_closesocket(*sock);
|
||||
*sock = INVALID_SOCKET;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_SCTP
|
||||
if (protocol == IPPROTO_SCTP) {
|
||||
/*
|
||||
|
@ -123,12 +157,27 @@ int init_client(int *sock, const char *host, const char *port,
|
|||
}
|
||||
|
||||
if (*sock == INVALID_SOCKET) {
|
||||
if (bindaddr != NULL && !found) {
|
||||
BIO_printf(bio_err, "Can't bind %saddress for %s%s%s\n",
|
||||
BIO_ADDRINFO_family(res) == AF_INET6 ? "IPv6 " :
|
||||
BIO_ADDRINFO_family(res) == AF_INET ? "IPv4 " :
|
||||
BIO_ADDRINFO_family(res) == AF_UNIX ? "unix " : "",
|
||||
bindhost != NULL ? bindhost : "",
|
||||
bindport != NULL ? ":" : "",
|
||||
bindport != NULL ? bindport : "");
|
||||
ERR_clear_error();
|
||||
ret = 0;
|
||||
}
|
||||
ERR_print_errors(bio_err);
|
||||
} else {
|
||||
/* Remove any stale errors from previous connection attempts */
|
||||
ERR_clear_error();
|
||||
ret = 1;
|
||||
}
|
||||
out:
|
||||
if (bindaddr != NULL) {
|
||||
BIO_ADDRINFO_free (bindaddr);
|
||||
}
|
||||
BIO_ADDRINFO_free(res);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,55 @@ int BIO_connect(int sock, const BIO_ADDR *addr, int options)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*-
|
||||
* BIO_bind - bind socket to address
|
||||
* @sock: the socket to set
|
||||
* @addr: local address to bind to
|
||||
* @options: BIO socket options
|
||||
*
|
||||
* Binds to the address using the given socket and options.
|
||||
*
|
||||
* Options can be a combination of the following:
|
||||
* - BIO_SOCK_REUSEADDR: Try to reuse the address and port combination
|
||||
* for a recently closed port.
|
||||
*
|
||||
* When restarting the program it could be that the port is still in use. If
|
||||
* you set to BIO_SOCK_REUSEADDR option it will try to reuse the port anyway.
|
||||
* It's recommended that you use this.
|
||||
*/
|
||||
int BIO_bind(int sock, const BIO_ADDR *addr, int options)
|
||||
{
|
||||
int on = 1;
|
||||
|
||||
if (sock == -1) {
|
||||
BIOerr(BIO_F_BIO_BIND, BIO_R_INVALID_SOCKET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
# ifndef OPENSSL_SYS_WINDOWS
|
||||
/*
|
||||
* SO_REUSEADDR has different behavior on Windows than on
|
||||
* other operating systems, don't set it there.
|
||||
*/
|
||||
if (options & BIO_SOCK_REUSEADDR) {
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const void *)&on, sizeof(on)) != 0) {
|
||||
SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
|
||||
BIOerr(BIO_F_BIO_BIND, BIO_R_UNABLE_TO_REUSEADDR);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
if (bind(sock, BIO_ADDR_sockaddr(addr), BIO_ADDR_sockaddr_size(addr)) != 0) {
|
||||
SYSerr(SYS_F_BIND, get_last_socket_error());
|
||||
BIOerr(BIO_F_BIO_BIND, BIO_R_UNABLE_TO_BIND_SOCKET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-
|
||||
* BIO_listen - Creates a listen socket
|
||||
* @sock: the socket to listen with
|
||||
|
@ -174,21 +223,6 @@ int BIO_listen(int sock, const BIO_ADDR *addr, int options)
|
|||
if (!BIO_socket_nbio(sock, (options & BIO_SOCK_NONBLOCK) != 0))
|
||||
return 0;
|
||||
|
||||
# ifndef OPENSSL_SYS_WINDOWS
|
||||
/*
|
||||
* SO_REUSEADDR has different behavior on Windows than on
|
||||
* other operating systems, don't set it there.
|
||||
*/
|
||||
if (options & BIO_SOCK_REUSEADDR) {
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const void *)&on, sizeof(on)) != 0) {
|
||||
SYSerr(SYS_F_SETSOCKOPT, get_last_socket_error());
|
||||
BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_REUSEADDR);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
if (options & BIO_SOCK_KEEPALIVE) {
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
|
||||
(const void *)&on, sizeof(on)) != 0) {
|
||||
|
@ -223,11 +257,8 @@ int BIO_listen(int sock, const BIO_ADDR *addr, int options)
|
|||
}
|
||||
# endif
|
||||
|
||||
if (bind(sock, BIO_ADDR_sockaddr(addr), BIO_ADDR_sockaddr_size(addr)) != 0) {
|
||||
SYSerr(SYS_F_BIND, get_last_socket_error());
|
||||
BIOerr(BIO_F_BIO_LISTEN, BIO_R_UNABLE_TO_BIND_SOCKET);
|
||||
if (!BIO_bind(sock, addr, options))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (socktype != SOCK_DGRAM && listen(sock, MAX_LISTEN) == -1) {
|
||||
SYSerr(SYS_F_LISTEN, get_last_socket_error());
|
||||
|
|
|
@ -19,6 +19,7 @@ static const ERR_STRING_DATA BIO_str_functs[] = {
|
|||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ACCEPT, 0), "BIO_accept"},
|
||||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ACCEPT_EX, 0), "BIO_accept_ex"},
|
||||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_ADDR_NEW, 0), "BIO_ADDR_new"},
|
||||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_BIND, 0), "BIO_bind"},
|
||||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_CALLBACK_CTRL, 0), "BIO_callback_ctrl"},
|
||||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_CONNECT, 0), "BIO_connect"},
|
||||
{ERR_PACK(ERR_LIB_BIO, BIO_F_BIO_CTRL, 0), "BIO_ctrl"},
|
||||
|
|
|
@ -116,6 +116,7 @@ BIO_F_ADDR_STRINGS:134:addr_strings
|
|||
BIO_F_BIO_ACCEPT:101:BIO_accept
|
||||
BIO_F_BIO_ACCEPT_EX:137:BIO_accept_ex
|
||||
BIO_F_BIO_ADDR_NEW:144:BIO_ADDR_new
|
||||
BIO_F_BIO_BIND:147:BIO_bind
|
||||
BIO_F_BIO_CALLBACK_CTRL:131:BIO_callback_ctrl
|
||||
BIO_F_BIO_CONNECT:138:BIO_connect
|
||||
BIO_F_BIO_CTRL:103:BIO_ctrl
|
||||
|
|
|
@ -10,6 +10,7 @@ s_client - SSL/TLS client program
|
|||
B<openssl> B<s_client>
|
||||
[B<-help>]
|
||||
[B<-connect host:port>]
|
||||
[B<-bind host:port>]
|
||||
[B<-proxy host:port>]
|
||||
[B<-unix path>]
|
||||
[B<-4>]
|
||||
|
@ -147,6 +148,12 @@ select the host and port using the optional target positional argument instead.
|
|||
If neither this nor the target positonal argument are specified then an attempt
|
||||
is made to connect to the local host on port 4433.
|
||||
|
||||
=item B<-bind host:port>]
|
||||
|
||||
This specifies the host address and or port to bind as the source for the
|
||||
connection. For Unix-domain sockets the port is ignored and the host is
|
||||
used as the source socket address.
|
||||
|
||||
=item B<-proxy host:port>
|
||||
|
||||
When used with the B<-connect> flag, the program uses the host and port
|
||||
|
@ -686,6 +693,9 @@ applications should B<not> do this as it makes them vulnerable to a MITM
|
|||
attack. This behaviour can be changed by with the B<-verify_return_error>
|
||||
option: any verify errors are then returned aborting the handshake.
|
||||
|
||||
The B<-bind> option may be useful if the server or a firewall requires
|
||||
connections to come from some particular address and or port.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Because this program has a lot of options and also because some of the
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
=head1 NAME
|
||||
|
||||
BIO_socket, BIO_connect, BIO_listen, BIO_accept_ex, BIO_closesocket - BIO
|
||||
BIO_socket, BIO_bind, BIO_connect, BIO_listen, BIO_accept_ex, BIO_closesocket - BIO
|
||||
socket communication setup routines
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
@ -10,6 +10,7 @@ socket communication setup routines
|
|||
#include <openssl/bio.h>
|
||||
|
||||
int BIO_socket(int domain, int socktype, int protocol, int options);
|
||||
int BIO_bind(int sock, const BIO_ADDR *addr, int options);
|
||||
int BIO_connect(int sock, const BIO_ADDR *addr, int options);
|
||||
int BIO_listen(int sock, const BIO_ADDR *addr, int options);
|
||||
int BIO_accept_ex(int accept_sock, BIO_ADDR *peer, int options);
|
||||
|
@ -21,6 +22,10 @@ BIO_socket() creates a socket in the domain B<domain>, of type
|
|||
B<socktype> and B<protocol>. Socket B<options> are currently unused,
|
||||
but is present for future use.
|
||||
|
||||
BIO_bind() binds the source address and service to a socket and
|
||||
may be useful before calling BIO_connect(). The options may include
|
||||
B<BIO_SOCK_REUSADDR>, which is described in L</FLAGS> below.
|
||||
|
||||
BIO_connect() connects B<sock> to the address and service given by
|
||||
B<addr>. Connection B<options> may be zero or any combination of
|
||||
B<BIO_SOCK_KEEPALIVE>, B<BIO_SOCK_NONBLOCK> and B<BIO_SOCK_NODELAY>.
|
||||
|
@ -81,7 +86,7 @@ BIO_socket() returns the socket number on success or B<INVALID_SOCKET>
|
|||
(-1) on error. When an error has occurred, the OpenSSL error stack
|
||||
will hold the error data and errno has the system error.
|
||||
|
||||
BIO_connect() and BIO_listen() return 1 on success or 0 on error.
|
||||
BIO_bind(), BIO_connect() and BIO_listen() return 1 on success or 0 on error.
|
||||
When an error has occurred, the OpenSSL error stack will hold the error
|
||||
data and errno has the system error.
|
||||
|
||||
|
@ -102,7 +107,7 @@ L<BIO_ADDR(3)>
|
|||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
|
||||
Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the OpenSSL license (the "License"). You may not use
|
||||
this file except in compliance with the License. You can obtain a copy
|
||||
|
|
|
@ -709,6 +709,7 @@ int BIO_sock_info(int sock,
|
|||
|
||||
int BIO_socket(int domain, int socktype, int protocol, int options);
|
||||
int BIO_connect(int sock, const BIO_ADDR *addr, int options);
|
||||
int BIO_bind(int sock, const BIO_ADDR *addr, int options);
|
||||
int BIO_listen(int sock, const BIO_ADDR *addr, int options);
|
||||
int BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options);
|
||||
int BIO_closesocket(int sock);
|
||||
|
|
|
@ -24,6 +24,7 @@ int ERR_load_BIO_strings(void);
|
|||
# define BIO_F_BIO_ACCEPT 101
|
||||
# define BIO_F_BIO_ACCEPT_EX 137
|
||||
# define BIO_F_BIO_ADDR_NEW 144
|
||||
# define BIO_F_BIO_BIND 147
|
||||
# define BIO_F_BIO_CALLBACK_CTRL 131
|
||||
# define BIO_F_BIO_CONNECT 138
|
||||
# define BIO_F_BIO_CTRL 103
|
||||
|
|
|
@ -4504,3 +4504,4 @@ RAND_DRBG_bytes 4445 1_1_1 EXIST::FUNCTION:
|
|||
RAND_DRBG_secure_new 4446 1_1_1 EXIST::FUNCTION:
|
||||
OSSL_STORE_vctrl 4447 1_1_1 EXIST::FUNCTION:
|
||||
X509_get0_authority_key_id 4448 1_1_0h EXIST::FUNCTION:
|
||||
BIO_bind 4449 1_1_1 EXIST::FUNCTION:SOCK
|
||||
|
|
Loading…
Reference in a new issue