diff --git a/inc_internal/utils.h b/inc_internal/utils.h index 79ccff2c..cf436fc0 100644 --- a/inc_internal/utils.h +++ b/inc_internal/utils.h @@ -1,18 +1,16 @@ -/* -Copyright 2019-2020 NetFoundry, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ +// Copyright (c) 2022. NetFoundry Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef ZITI_TLS_UTILS_H #define ZITI_TLS_UTILS_H @@ -24,6 +22,7 @@ limitations under the License. #include #include #include +#include "ziti/model_collections.h" #ifdef __cplusplus extern "C" { @@ -108,11 +107,21 @@ if (!uv_is_closing((uv_handle_t*)(h))) uv_close((uv_handle_t*)(h), (uv_close_cb) }}while(0) - void ziti_alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); int get_url_data(const char *url, struct http_parser_url *parser, int uf, char *out, size_t maxout); +/** + * Split string based on delimiters. + * strings are appended to the provided list. Caller is responsible to freeing resulting strings - + * possibly via `model_list_clear(result, free)` + * @param str + * @param delim + * @param result + * @return number of tokens + */ +extern size_t str_split(const char *str, const char *delim, model_list *result); + #ifdef __cplusplus } #endif diff --git a/library/utils.c b/library/utils.c index 8e77a9c2..5119cf55 100644 --- a/library/utils.c +++ b/library/utils.c @@ -1,18 +1,16 @@ -/* -Copyright 2019-2020 NetFoundry, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ +// Copyright (c) 2022. NetFoundry Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include @@ -405,10 +403,33 @@ void hexify(const uint8_t *bin, size_t bin_len, char sep, char **buf) { char *p = out; for (int i = 0; i < bin_len; i++) { unsigned char b = bin[i]; - if (sep && i > 0) *p++ = sep; + if (sep && i > 0) { *p++ = sep; } *p++ = hex[b >> 4]; *p++ = hex[b & 0xf]; } *p = 0; *buf = out; +} + + +size_t str_split(const char *str, const char *delim, model_list *result) { + size_t count = 0; + if (str) { + const char *sep = str; + do { + const char *s = sep; + char *val; + if ((sep = strpbrk(s, delim)) != NULL) { + size_t tok_len = sep++ - s; + val = calloc(1, tok_len + 1); + strncpy(val, s, tok_len); + } else { + val = strdup(s); + } + model_list_append(result, val); + count++; + } while (sep); + } + + return count; } \ No newline at end of file diff --git a/programs/ziti-prox-c/proxy.c b/programs/ziti-prox-c/proxy.c index cdc0973f..99560479 100644 --- a/programs/ziti-prox-c/proxy.c +++ b/programs/ziti-prox-c/proxy.c @@ -57,10 +57,17 @@ static struct sig_handlers { struct proxy_app_ctx { model_map listeners; + model_map bindings; LIST_HEAD(clients, client) clients; ziti_context ziti; }; +struct binding { + char *service_name; + ziti_connection conn; + struct addrinfo *addr; +}; + struct listener { char *service_name; int port; @@ -133,10 +140,20 @@ static void process_stop(uv_loop_t *loop, struct proxy_app_ctx *app_ctx) { } static void debug_dump(struct proxy_app_ctx *app_ctx) { + printf("==== listeners ====\n"); MODEL_MAP_FOR(it, app_ctx->listeners) { struct listener *l = model_map_it_value(it); printf("listening for service[%s] on port[%d]\n", l->service_name, l->port); } + + printf("\n==== bindings ====\n"); + MODEL_MAP_FOR(it, app_ctx->bindings) { + struct binding *b = model_map_it_value(it); + char addr[24]; + uv_getnameinfo_t name; + uv_getnameinfo(global_loop, &name, NULL, b->addr->ai_addr, NI_NUMERICHOST); + printf("bound to service[%s] -> %s:%s\n", b->service_name, name.host, name.service); + } ziti_dump(app_ctx->ziti, fprintf, stdout); } @@ -283,6 +300,58 @@ static void update_listener(ziti_service *service, int status, struct listener * } } +static void binding_listen_cb(ziti_connection srv, int status) { + struct binding *b = ziti_conn_data(srv); + if (status != ZITI_OK) { + ZITI_LOG(WARN, "failed to bind to service[%s]", b->service_name); + ziti_close(b->conn, NULL); + b->conn = NULL; + } +} + +static void on_ziti_accept(ziti_connection clt, int status) { + uv_stream_t *s = ziti_conn_data(clt); + if (status == ZITI_OK) { + ziti_conn_bridge(clt, s, on_bridge_close); + } else { + ziti_close(clt, NULL); + uv_close(s, on_bridge_close); + } +} + +static void on_tcp_connect(uv_connect_t *conn_req, int status) { + ziti_connection clt = conn_req->data; + if (status == 0) { + ziti_conn_set_data(clt, conn_req->handle); + ziti_accept(clt, on_ziti_accept, NULL); + } else { + uv_close((uv_handle_t *) conn_req->handle, on_bridge_close); + ziti_close(clt, NULL); + } + free(conn_req); +} + +static void binding_client_cb(ziti_connection srv, ziti_connection clt, int status, ziti_client_ctx *clt_ctx) { + struct binding *b = ziti_conn_data(srv); + + if (status == ZITI_OK) { + NEWP(tcp, uv_tcp_t); + uv_tcp_init(global_loop, tcp); + + NEWP(conn_req, uv_connect_t); + conn_req->data = clt; + if (uv_tcp_connect(conn_req, tcp, b->addr->ai_addr, on_tcp_connect) != 0) { + ziti_close(clt, NULL); + uv_close((uv_handle_t *) tcp, (uv_close_cb) free); + free(conn_req); + } + } else { + ZITI_LOG(WARN, "stopping serving[%s] due to %d/%s", b->service_name, status, ziti_errorstr(status)); + ziti_close(b->conn, NULL); + b->conn = NULL; + } +} + static void service_check_cb(ziti_context ztx, ziti_service *service, int status, void *ctx) { struct proxy_app_ctx *app_ctx = ctx; ZITI_LOG(DEBUG, "service[%s]: %s", service->name, ziti_errorstr(status)); @@ -290,6 +359,14 @@ static void service_check_cb(ziti_context ztx, ziti_service *service, int status if (l) { update_listener(service, status, l); } + + struct binding *b = model_map_get(&app_ctx->bindings, service->name); + if (b && (service->perm_flags & ZITI_CAN_BIND) != 0) { + if (b->conn == NULL) { + ziti_conn_init(ztx, &b->conn, b); + ziti_listen(b->conn, b->service_name, binding_listen_cb, binding_client_cb); + } + } } static void on_ziti_event(ziti_context ztx, const ziti_event_t *event) { @@ -440,7 +517,7 @@ void mfa_auth_event_handler(ziti_context ztx) { uv_queue_work(global_loop, &mfa_wr->w, mfa_worker, mfa_worker_done); } - +static struct proxy_app_ctx app_ctx = {0}; void run(int argc, char **argv) { PREPF(uv, uv_strerror); @@ -449,7 +526,7 @@ void run(int argc, char **argv) { uv_loop_init(loop); global_loop = loop; - struct proxy_app_ctx app_ctx = {0}; + for (int i = 0; i < argc; i++) { char *p = strchr(argv[i], ':'); @@ -533,7 +610,8 @@ int run_opts(int argc, char **argv) { {"debug", optional_argument, NULL, 'd'}, {"config", required_argument, NULL, 'c'}, {"metrics", optional_argument, NULL, 'm'}, - {NULL, 0, NULL, 0} + {"bind", required_argument, NULL, 'b'}, + {NULL, 0, NULL, 0} }; int c, option_index, errors = 0; @@ -542,7 +620,7 @@ int run_opts(int argc, char **argv) { optind = 0; - while ((c = getopt_long(argc, argv, "d:c:m:", + while ((c = getopt_long(argc, argv, "b:c:d:m:", long_options, &option_index)) != -1) { switch (c) { case 'd': @@ -565,6 +643,45 @@ int run_opts(int argc, char **argv) { } break; + case 'b': + if (!optarg) { + fprintf(stderr, "-b|--bind option requires argument\n"); + errors++; + break; + } + + model_list args = {0}; + str_split(optarg, ":", &args); + size_t args_len = model_list_size(&args); + if (args_len < 2) { + fprintf(stderr, "-b|--bind option should be \n"); + errors++; + break; + } + model_list_iter it = model_list_iterator(&args); + NEWP(b, struct binding); + b->service_name = model_list_it_element(it); + it = model_list_it_remove(it); + + if (model_list_size(&args) > 1) { + char *host = model_list_it_element(it); + if (strlen(host) == 0) { + host = "localhost"; + } + it = model_list_it_next(it); + char *port = model_list_it_element(it); + + int rc = getaddrinfo(host, port, NULL, &b->addr); + if (rc != 0) { + errors++; + fprintf(stderr, "failed to resolve %s:%s for service[%s] binding", host, port, b->service_name); + } + model_map_set(&app_ctx.bindings, b->service_name, b); + } + + model_list_clear(&args, free); + break; + default: { fprintf(stderr, "Unknown option \"%c\"\n", c); errors++;