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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ Configuration Syntax
# Combining regular expression and wildcard will resolve the hostname
# client requested and proxy to it
.*\\.edu *:443
# Also possible to rewrite target from source using capture groups
([a-z]+)\.public\.com $1.internal.local
}

DNS Resolution
Expand Down
32 changes: 32 additions & 0 deletions src/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct Address {
HOSTNAME,
SOCKADDR,
WILDCARD,
PATTERN,
} type;

size_t len; /* length of data */
Expand Down Expand Up @@ -123,6 +124,20 @@ new_address(const char *hostname_or_ip) {
}
}

if (strchr(hostname_or_ip, '$') != NULL) {
len = strlen(hostname_or_ip);
struct Address *addr = malloc(
offsetof(struct Address, data) + len + 1);
if (addr != NULL) {
addr->type = PATTERN;
addr->port = 0;
addr->len = len;
memcpy(addr->data, hostname_or_ip, len);
addr->data[addr->len] = '\0';
}
return addr;
}

/* Wildcard */
if (strcmp("*", hostname_or_ip) == 0) {
struct Address *addr = malloc(sizeof(struct Address));
Expand Down Expand Up @@ -214,6 +229,7 @@ size_t
address_len(const struct Address *addr) {
switch (addr->type) {
case HOSTNAME:
case PATTERN:
/* include trailing null byte */
return offsetof(struct Address, data) + addr->len + 1;
case SOCKADDR:
Expand Down Expand Up @@ -274,6 +290,11 @@ address_is_wildcard(const struct Address *addr) {
return addr != NULL && addr->type == WILDCARD;
}

int
address_is_pattern(const struct Address * addr) {
return addr != NULL && addr->type == PATTERN;
}

const char *
address_hostname(const struct Address *addr) {
if (addr->type != HOSTNAME)
Expand All @@ -282,6 +303,14 @@ address_hostname(const struct Address *addr) {
return addr->data;
}

const char *
address_pattern(const struct Address *addr) {
if (addr->type != PATTERN)
return NULL;

return addr->data;
}

const struct sockaddr *
address_sa(const struct Address *addr) {
if (addr->type != SOCKADDR)
Expand Down Expand Up @@ -319,6 +348,7 @@ address_port(const struct Address *addr) {
return 0;
}
case WILDCARD:
case PATTERN:
return addr->port;
default:
/* invalid Address type */
Expand Down Expand Up @@ -350,6 +380,7 @@ address_set_port(struct Address *addr, uint16_t port) {
/* fall through */
case HOSTNAME:
case WILDCARD:
case PATTERN:
addr->port = port;
break;
default:
Expand All @@ -375,6 +406,7 @@ display_address(const struct Address *addr, char *buffer, size_t buffer_len) {

switch (addr->type) {
case HOSTNAME:
case PATTERN:
if (addr->port != 0)
snprintf(buffer, buffer_len, "%s:%" PRIu16,
addr->data,
Expand Down
2 changes: 2 additions & 0 deletions src/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ struct Address *copy_address(const struct Address *);
size_t address_len(const struct Address *);
int address_compare(const struct Address *, const struct Address *);
int address_is_hostname(const struct Address *);
int address_is_pattern(const struct Address *);
int address_is_sockaddr(const struct Address *);
int address_is_wildcard(const struct Address *);
const char *address_hostname(const struct Address *);
const char *address_pattern(const struct Address *);
const struct sockaddr *address_sa(const struct Address *);
socklen_t address_sa_len(const struct Address *);
uint16_t address_port(const struct Address *);
Expand Down
57 changes: 51 additions & 6 deletions src/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ init_backend(struct Backend *backend) {
if (backend->pattern_re == NULL) {
const char *reerr;
int reerroffset;

backend->pattern_re =
pcre_compile(backend->pattern, 0, &reerr, &reerroffset, NULL);
if (backend->pattern_re == NULL) {
Expand All @@ -117,8 +116,9 @@ init_backend(struct Backend *backend) {
return 1;
}

struct Backend *
struct BackendLookupResult
lookup_backend(const struct Backend_head *head, const char *name, size_t name_len) {
struct BackendLookupResult result;
struct Backend *iter;

if (name == NULL) {
Expand All @@ -128,12 +128,57 @@ lookup_backend(const struct Backend_head *head, const char *name, size_t name_le

STAILQ_FOREACH(iter, head, entries) {
assert(iter->pattern_re != NULL);
if (pcre_exec(iter->pattern_re, NULL,
name, name_len, 0, 0, NULL, 0) >= 0)
return iter;

if ((result.matches[0] = pcre_exec(iter->pattern_re, NULL,
name, name_len, 0, 0, result.matches + 1, 31)) >= 0) {

result.backend = iter;
return result;
}
}

return NULL;
result.backend = NULL;

return result;
}

int apply_pattern(const char * name, const char * pattern, int * matches, char * dst, size_t result_len)
{
int length = result_len > 1024 ? 1024 : result_len;
const char * src = pattern;
while(length > 0 && (*src)) {
if (src[0] == '$' && src[1] != 0) {
if (src[1] >= '0' && src[1] <= '9') {
int stringnumber = src[1] - '0';
int ret;
src += 2;

*dst=0;
ret = pcre_copy_substring(name, matches + 1, matches[0], stringnumber, dst, length);
if (ret < 0) {
return 0;
}
dst += ret;
length -= ret;
} else {
*dst = src[1];
dst++;
length--;
src += 2;
}

} else {
*dst = *src;
dst++;
src++;
length--;
}
}
if (length) {
*dst = 0;
return 1;
}
return 0;
}

void
Expand Down
8 changes: 7 additions & 1 deletion src/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ struct Backend {
STAILQ_ENTRY(Backend) entries;
};

struct BackendLookupResult {
struct Backend * backend;
int matches[32];
};

void add_backend(struct Backend_head *, struct Backend *);
int init_backend(struct Backend *);
struct Backend *lookup_backend(const struct Backend_head *, const char *, size_t);
struct BackendLookupResult lookup_backend(const struct Backend_head *, const char *, size_t);
void print_backend_config(FILE *, const struct Backend *);
void remove_backend(struct Backend_head *, struct Backend *);
struct Backend *new_backend();
int accept_backend_arg(struct Backend *, const char *);
int apply_pattern(const char * name, const char * pattern, int * matches, char *, size_t);


#endif
45 changes: 45 additions & 0 deletions src/listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,51 @@ listener_lookup_server_address(const struct Listener *listener,
.address = listener->fallback_address,
.use_proxy_header = listener->fallback_use_proxy_header
};
} else if (address_is_pattern(table_result.address)) {
/* Pattern table entry, create a new address from hostname */
char replacement[1024];
if (apply_pattern(name, address_pattern(table_result.address) ,table_result.matches, replacement, 1024) == 0) {
warn("Failed pattern %.*s in client request",
(int)name_len, name);

return (struct LookupResult){
.address = listener->fallback_address,
.use_proxy_header = listener->fallback_use_proxy_header
};
}
struct Address *new_addr = new_address(replacement);
if (new_addr == NULL) {
warn("Failed pattern%.*s in client request",
(int)name_len, name);

return (struct LookupResult){
.address = listener->fallback_address,
.use_proxy_header = listener->fallback_use_proxy_header
};
} else if (address_is_sockaddr(new_addr)) {
warn("Refusing to proxy to socket address literal %.*s in request",
(int)name_len, name);
free(new_addr);

return (struct LookupResult){
.address = listener->fallback_address,
.use_proxy_header = listener->fallback_use_proxy_header
};
}

/* We created a valid new_addr, use the port from wildcard address if
* present otherwise the listener */
address_set_port(new_addr, address_port(table_result.address) != 0 ?
address_port(table_result.address) :
address_port(listener->address));


return (struct LookupResult){
.address = new_addr,
.caller_free_address = 1,
.use_proxy_header = table_result.use_proxy_header
};

} else if (address_is_wildcard(table_result.address)) {
/* Wildcard table entry, create a new address from hostname */
struct Address *new_addr = new_address(name);
Expand Down
16 changes: 11 additions & 5 deletions src/table.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
static void free_table(struct Table *);


static inline struct Backend *
static inline struct BackendLookupResult
table_lookup_backend(const struct Table *table, const char *name, size_t name_len) {
return lookup_backend(&table->backends, name, name_len);
}
Expand Down Expand Up @@ -130,14 +130,20 @@ remove_table(struct Table_head *tables, struct Table *table) {

struct LookupResult
table_lookup_server_address(const struct Table *table, const char *name, size_t name_len) {
struct Backend *b = table_lookup_backend(table, name, name_len);
if (b == NULL) {
struct BackendLookupResult b = table_lookup_backend(table, name, name_len);
if (b.backend == NULL) {
info("No match found for %.*s", (int)name_len, name);
return (struct LookupResult){.address = NULL};
}

return (struct LookupResult){.address = b->address,
.use_proxy_header = b->use_proxy_header};
struct LookupResult result;
result.address = b.backend->address;
result.caller_free_address = 0;
result.use_proxy_header = b.backend->use_proxy_header;
for(int i = 0; i < 32; ++i) {
result.matches[i] = b.matches[i];
}
return result;
}

void
Expand Down
1 change: 1 addition & 0 deletions src/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct LookupResult {
const struct Address *address;
int caller_free_address;
int use_proxy_header;
int matches[32];
};

struct Table *new_table();
Expand Down