Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions configs/client/burp.conf.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ progress_counter = 1
# contacting the server on a timed backup.
# randomise = 1200

# If 'server' is a dns name with multipe ip address, with this option you can
# shuffle it to randomize the connection regardless of the order what you got
# from the DNS. The default is 0 (not enabled). To enable, change it to 1.
# server_randomize = 0

# If server_randomize is enabled, here you can specify what address type you
# prefer. These type of addressess will be prior others.
# The default is 0 (no prior), it can be 4 if you prefer IPv4, or 6 if IPv6.
# server_randomize_prefer = 0

# Set server_can_restore to 0 if you do not want the server to be able to
# initiate a restore (setting it to 1 also requires 'restoreprefix').
server_can_restore = 0
Expand Down
6 changes: 6 additions & 0 deletions manpages/burp.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,12 @@ Required to run in client mode.
\fBserver=[host:port]\fR
Defines the server to connect to. If you don't specify a port here, you will need to specify it separately.
.TP
\fBserver_randomize=[0|1]\fR
If server host is a hostname (i.e. backup.company.com) with multiple IP addresses, with this option you can shuffle the result what give getaddrinfo(). Default is 0 - disabled.
.TP
\fBserver_randomize_preferred=[0|4|6]\fR
When server_randomize enabled, you can define which address type you prefer. 4 - IPv4, 6 - IPv6. Default is 0, which means v4 and v6 address are with equal weight, so it shuffle the elements regardless to the address type. Example: If you have 3 IPv6 and 3 IPv4 address behind the server hostname, and you prefer IPv4, that means in the first 3 place the IPv4 addressess will be (in random order), and then in the next 3 place the IPv6 address (in random order).
.TP
\fBserver_failover=[host:port]\fR
Defines a failover server to connect to. You can provide more than one 'server_failover' entry. The client will try the next failover server if it fails to connect. IMPORTANT: The burp client currently does not have the capability to have a different SSL profile for each server, so for this to work for you, you will need to have the same CA and certificates on each server.
.TP
Expand Down
2 changes: 1 addition & 1 deletion src/client/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ static int ssl_setup(int *rfd, SSL **ssl, SSL_CTX **ctx,
}

snprintf(portstr, sizeof(portstr), "%d", port);
if((*rfd=init_client_socket(server_copy, portstr))<0)
if((*rfd=init_client_socket(server_copy, portstr, confs))<0)
goto end;

if(!(*ssl=SSL_new(*ctx))
Expand Down
4 changes: 4 additions & 0 deletions src/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ static int reset_conf(struct conf **c, enum conf_opt o)
return sc_str(c[o], 0, 0, "passwd");
case OPT_SERVER:
return sc_str(c[o], 0, 0, "server");
case OPT_SERVER_RANDOMIZE:
return sc_int(c[o], 0, 0, "server_randomize");
case OPT_SERVER_RANDOMIZE_PREFER:
return sc_int(c[o], 0, 0, "server_randomize_prefer");
case OPT_SERVER_FAILOVER:
return sc_lst(c[o], 0, 0, "server_failover");
case OPT_FAILOVER_ON_BACKUP_ERROR:
Expand Down
2 changes: 2 additions & 0 deletions src/conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ enum conf_opt
OPT_PASSWD, // also a clientconfdir option
OPT_ENABLED, // also a clientconfdir option
OPT_SERVER,
OPT_SERVER_RANDOMIZE,
OPT_SERVER_RANDOMIZE_PREFER,
OPT_SERVER_FAILOVER,
OPT_FAILOVER_ON_BACKUP_ERROR,
OPT_ENCRYPTION_PASSWORD,
Expand Down
175 changes: 175 additions & 0 deletions src/gai_rand.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <netdb.h>

#ifdef HAVE_WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

typedef enum {
ADDR_PREF_EQUAL = 0, // no preference, shuffle all
ADDR_PREF_IPV4, // IPv4 first (random inside group), then others
ADDR_PREF_IPV6 // IPv6 first (random inside group), then others
} addr_pref_t;

static void fisher_yates(struct addrinfo **arr, size_t n)
{
/* Not need for <2 item */
if (n < 2)
return;

for (size_t i = n - 1; i > 0; i--) {
size_t j = (size_t)(rand() % (i + 1)); // 0 <= j <= i
struct addrinfo *tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}

static int is_preferred(const struct addrinfo *ai, addr_pref_t pref)
{
/* -1 means unknown address type */
int preferred = -1;

if (pref == ADDR_PREF_IPV4) {
if (ai->ai_family == AF_INET)
preferred = 1;
else if (ai->ai_family == AF_INET6)
preferred = 0;
}

if (pref == ADDR_PREF_IPV6) {
if (ai->ai_family == AF_INET6)
preferred = 1;
else if (ai->ai_family == AF_INET)
preferred = 0;
}

return preferred;
}

/*
* Shuffle the order of an addrinfo list in-place,
* with optional preference for IPv4 or IPv6.
*
* pref == ADDR_PREF_EQUAL:
* all addresses shuffled together
*
* pref == ADDR_PREF_IPV4:
* IPv4 addresses come first (randomized among themselves),
* then all non-IPv4 (IPv6/others), randomized among themselves.
*
* pref == ADDR_PREF_IPV6:
* IPv6 addresses come first (randomized among themselves),
* then all non-IPv6 (IPv4/others), randomized among themselves.
*
* NOTE: Seed RNG once e.g.:
* srand((unsigned)time(NULL));
*
*/
void shuffle_addrinfo(struct addrinfo **res, addr_pref_t pref)
{
if (res == NULL || *res == NULL)
return;

struct addrinfo *cur;
size_t n = 0;

/* First, count nodes */
for (cur = *res; cur; cur = cur->ai_next)
n++;

/* Less than 2, not need to shuffle */
if (n < 2)
return;

/* Initialize srand() function */
srand((unsigned)time(NULL));

/* v4 and v6 addresses are equals, shuffle all */
if (pref == ADDR_PREF_EQUAL) {
struct addrinfo **arr = malloc(n * sizeof(*arr));
if (!arr)
return;

size_t i = 0;
for (cur = *res; cur; cur = cur->ai_next)
arr[i++] = cur;

fisher_yates(arr, n);

for (i = 0; i < n - 1; i++)
arr[i]->ai_next = arr[i + 1];
arr[n - 1]->ai_next = NULL;

*res = arr[0];
free(arr);
return;
}

/* Preference mode: partition into preferred + others */
size_t n_pref = 0, n_other = 0;

for (cur = *res; cur; cur = cur->ai_next) {
if (is_preferred(cur, pref)==1)
n_pref++;
else if (is_preferred(cur, pref)==0)
n_other++;
}

struct addrinfo **pref_arr = malloc(n_pref * sizeof(*pref_arr));
struct addrinfo **other_arr = malloc(n_other * sizeof(*other_arr));
if (!pref_arr || !other_arr) {
free(pref_arr);
free(other_arr);
return;
}

size_t ip = 0, io = 0;
for (cur = *res; cur; cur = cur->ai_next) {
if (is_preferred(cur, pref)==1)
pref_arr[ip++] = cur;
else if (is_preferred(cur, pref)==0)
other_arr[io++] = cur;
}

/* Shuffle each group separately */
fisher_yates(pref_arr, n_pref);
fisher_yates(other_arr, n_other);

/* Rebuild list: preferred first, then others */
struct addrinfo *head = NULL;
struct addrinfo *tail = NULL;

for (size_t i = 0; i < n_pref; i++) {
if (!head)
head = pref_arr[i];
else
tail->ai_next = pref_arr[i];
tail = pref_arr[i];
}

for (size_t i = 0; i < n_other; i++) {
if (!head)
head = other_arr[i];
else
tail->ai_next = other_arr[i];
tail = other_arr[i];
}

if (tail)
tail->ai_next = NULL;

*res = head;

free(pref_arr);
free(other_arr);
}
43 changes: 41 additions & 2 deletions src/handy.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "log.h"
#include "msg.h"
#include "prepend.h"
#include "gai_rand.c"

#include <sys/types.h>
#include <sys/socket.h>
Expand Down Expand Up @@ -265,10 +266,12 @@ int set_keepalive(int fd, int value)
return 0;
}

int init_client_socket(const char *host, const char *port)
int init_client_socket(const char *host, const char *port, struct conf **confs)
{
int rfd=-1;
int gai_ret;
addr_pref_t server_prefferred_address;
char ipstring[INET6_ADDRSTRLEN]; // in this length can fit v4 address too
struct addrinfo hints;
struct addrinfo *result;
struct addrinfo *rp;
Expand All @@ -287,12 +290,48 @@ int init_client_socket(const char *host, const char *port)
return -1;
}

// Randomize addrinfo array if enabled with the preferred address order
if (get_int(confs[OPT_SERVER_RANDOMIZE])) {
switch (get_int(confs[OPT_SERVER_RANDOMIZE_PREFER])) {
case 6:
logp("Server randomization enabled (IPv6 preferred)\n");
server_prefferred_address = ADDR_PREF_IPV6;
break;
case 4:
logp("Server randomization enabled (IPv4 preferred)\n");
server_prefferred_address = ADDR_PREF_IPV4;
break;
default:
server_prefferred_address = ADDR_PREF_EQUAL;
logp("Server randomization enabled\n");
break;
}
shuffle_addrinfo(&result,server_prefferred_address);
}

for(rp=result; rp; rp=rp->ai_next)
{
rfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if(rfd<0) continue;
set_keepalive(rfd, 1);
if(connect(rfd, rp->ai_addr, rp->ai_addrlen) != -1) break;

if (rp->ai_family == AF_INET) {
struct sockaddr_in *psai = (struct sockaddr_in*)rp->ai_addr;
// converting address to char (string)
inet_ntop(rp->ai_family, &(psai->sin_addr), ipstring, INET_ADDRSTRLEN);
} else if (rp->ai_family == AF_INET6) {
struct sockaddr_in6 *psai = (struct sockaddr_in6*)rp->ai_addr;
// converting address to char (string)
inet_ntop(rp->ai_family, &(psai->sin6_addr), ipstring, INET6_ADDRSTRLEN);
} else {
logp("Don't know how to convert family %d addresses\n", rp->ai_family);
}
logp("Trying to connect to %s:%s\n", ipstring, port);

if(connect(rfd, rp->ai_addr, rp->ai_addrlen) != -1) {
logp("Connected to %s:%s\n", ipstring, port);
break;
}
close_fd(&rfd);
}
freeaddrinfo(result);
Expand Down
2 changes: 1 addition & 1 deletion src/handy.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extern void add_fd_to_sets(int fd,
fd_set *read_set, fd_set *write_set, fd_set *err_set, int *max_fd);
extern int set_peer_env_vars(struct sockaddr_storage *addr);
extern int set_keepalive(int fd, int value);
extern int init_client_socket(const char *host, const char *port);
extern int init_client_socket(const char *host, const char *port, struct conf **confs);
extern void reuseaddr(int fd);
extern int chuser_and_or_chgrp(const char *user, const char *group, int readall);
extern int dpth_is_compressed(int compressed, const char *datapath);
Expand Down
2 changes: 2 additions & 0 deletions utest/test_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ static void check_default(struct conf **c, enum conf_opt o)
case OPT_MAX_PARALLEL_BACKUPS:
case OPT_TIMER_REPEAT_INTERVAL:
case OPT_REGEX_CASE_INSENSITIVE:
case OPT_SERVER_RANDOMIZE:
case OPT_SERVER_RANDOMIZE_PREFER:
case OPT_FORCE_UPDATE_ENCRYPTION:
fail_unless(get_int(c[o])==0);
break;
Expand Down