commit 738b3cc9ee5e087ffe860159c9e5c5c0bd4d1d7e from: rob date: Sun Sep 15 19:23:29 2019 UTC Add support for binary protocol health checking. Feedback and guidance from benno@ and reky@. Man page tweaks from jmc@. ok benno@ commit - bb2805c1367c443daabaa1ad8b5aa184c8745369 commit + 738b3cc9ee5e087ffe860159c9e5c5c0bd4d1d7e blob - 3a3ac92c29edcd93cd165466d578aec2136f4298 blob + 0a92712c2dff1cad112a5d2c19555d811bebde2a --- check_tcp.c +++ check_tcp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_tcp.c,v 1.56 2018/04/14 20:42:41 benno Exp $ */ +/* $OpenBSD: check_tcp.c,v 1.57 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard @@ -171,6 +171,7 @@ tcp_host_up(struct ctl_tcp_event *cte) cte->validate_read = NULL; cte->validate_close = check_http_digest; break; + case CHECK_BINSEND_EXPECT: case CHECK_SEND_EXPECT: cte->validate_read = check_send_expect; cte->validate_close = check_send_expect; @@ -182,8 +183,11 @@ tcp_host_up(struct ctl_tcp_event *cte) return; } - if (cte->table->sendbuf != NULL) { + if (cte->table->sendbuf != NULL && cte->table->sendbinbuf == NULL) { cte->req = cte->table->sendbuf; + } else if (cte->table->sendbinbuf != NULL) + cte->req = cte->table->sendbinbuf->buf; + if (cte->table->sendbuf != NULL || cte->table->sendbinbuf != NULL) { event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, tcp_send_req, &cte->tv_start, &cte->table->conf.timeout, cte); return; @@ -207,7 +211,15 @@ tcp_send_req(int s, short event, void *arg) hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT); return; } - len = strlen(cte->req); + + if (cte->table->sendbinbuf != NULL) { + len = ibuf_size(cte->table->sendbinbuf); + log_debug("%s: table %s sending binary", __func__, + cte->table->conf.name); + print_hex(cte->table->sendbinbuf->buf, 0, len); + } else + len = strlen(cte->req); + do { bs = write(s, cte->req, len); if (bs == -1) { @@ -289,17 +301,35 @@ check_send_expect(struct ctl_tcp_event *cte) { u_char *b; - /* - * ensure string is nul-terminated. - */ - b = ibuf_reserve(cte->buf, 1); - if (b == NULL) - fatal("out of memory"); - *b = '\0'; - if (fnmatch(cte->table->conf.exbuf, cte->buf->buf, 0) == 0) { - cte->host->he = HCE_SEND_EXPECT_OK; - cte->host->up = HOST_UP; - return (0); + if (cte->table->conf.check == CHECK_BINSEND_EXPECT) { + log_debug("%s: table %s expecting binary", + __func__, cte->table->conf.name); + print_hex(cte->table->conf.exbinbuf, 0, + strlen(cte->table->conf.exbuf) / 2); + + if (memcmp(cte->table->conf.exbinbuf, cte->buf->buf, + strlen(cte->table->conf.exbuf) / 2) == 0) { + cte->host->he = HCE_SEND_EXPECT_OK; + cte->host->up = HOST_UP; + return (0); + } else { + log_debug("%s: table %s received mismatching binary", + __func__, cte->table->conf.name); + print_hex(cte->buf->buf, 0, ibuf_size(cte->buf)); + } + } else if (cte->table->conf.check == CHECK_SEND_EXPECT) { + /* + * ensure string is nul-terminated. + */ + b = ibuf_reserve(cte->buf, 1); + if (b == NULL) + fatal("out of memory"); + *b = '\0'; + if (fnmatch(cte->table->conf.exbuf, cte->buf->buf, 0) == 0) { + cte->host->he = HCE_SEND_EXPECT_OK; + cte->host->up = HOST_UP; + return (0); + } } cte->host->he = HCE_SEND_EXPECT_FAIL; cte->host->up = HOST_UNKNOWN; blob - eb453507c8232d26e8dd17464b647a8b0b1f7c56 blob + 3fe829101f7e4f1a555cf07db958e841a0d309f2 --- check_tls.c +++ check_tls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_tls.c,v 1.1 2017/05/27 08:33:25 claudio Exp $ */ +/* $OpenBSD: check_tls.c,v 1.2 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2017 Claudio Jeker @@ -93,6 +93,7 @@ check_tls_write(int s, short event, void *arg) int retry_flag = EV_WRITE; int len; int ret; + void *buf; if (event == EV_TIMEOUT) { cte->host->up = HOST_DOWN; @@ -101,9 +102,19 @@ check_tls_write(int s, short event, void *arg) return; } - len = strlen(cte->table->sendbuf); + if (cte->table->sendbinbuf != NULL) { + len = ibuf_size(cte->table->sendbinbuf); + buf = cte->table->sendbinbuf->buf; + log_debug("%s: table %s sending binary", __func__, + cte->table->conf.name); + print_hex(cte->table->sendbinbuf->buf, 0, len); + } else { + len = strlen(cte->table->sendbuf); + buf = cte->table->sendbuf; + } - ret = tls_write(cte->tls, cte->table->sendbuf, len); + ret = tls_write(cte->tls, buf, len); + if (ret > 0) { if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) blob - 2b8a308a9699e3308c9677a5253e701ffe36738a blob + 3e60d63ef52437fed245ed2715e30a0b2bf7956b --- config.c +++ config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.40 2019/06/26 12:13:47 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.41 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2011 - 2014 Reyk Floeter @@ -367,6 +367,13 @@ config_gettable(struct relayd *env, struct imsg *imsg) return (-1); } } + if (tb->conf.check == CHECK_BINSEND_EXPECT) { + tb->sendbinbuf = string2binary(tb->sendbuf); + if (tb->sendbinbuf == NULL) { + free(tb); + return (-1); + } + } TAILQ_INIT(&tb->hosts); TAILQ_INSERT_TAIL(env->sc_tables, tb, entry); blob - c6e2bcacdfb3a19293dc2a020e908847c494401a blob + fc22377b94f6af1ad8eb23b63c9830819d693458 --- parse.y +++ parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.241 2019/07/13 06:54:45 chrisz Exp $ */ +/* $OpenBSD: parse.y,v 1.242 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter @@ -168,7 +168,7 @@ typedef struct { %} -%token ALL APPEND BACKLOG BACKUP BUFFER CA CACHE SET CHECK CIPHERS CODE +%token ALL APPEND BACKLOG BACKUP BINARY BUFFER CA CACHE SET CHECK CIPHERS CODE %token COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK EXTERNAL FILENAME %token FORWARD FROM HASH HEADER HEADERLEN HOST HTTP ICMP INCLUDE INET INET6 %token INTERFACE INTERVAL IP KEYPAIR LABEL LISTEN VALUE LOADBALANCE LOG LOOKUP @@ -389,6 +389,25 @@ sendbuf : NOTHING { } ; +sendbinbuf : NOTHING { + table->sendbinbuf = NULL; + } + | STRING { + if (strlen($1) == 0) { + yyerror("empty binary send data"); + free($1); + YYERROR; + } + table->sendbuf = strdup($1); + if (table->sendbuf == NULL) + fatal("out of memory"); + table->sendbinbuf = string2binary($1); + if (table->sendbinbuf == NULL) + fatal("failed in binary send data"); + free($1); + } + ; + main : INTERVAL NUMBER { if ((conf->sc_conf.interval.tv_sec = $2) < 0) { yyerror("invalid interval: %lld", $2); @@ -951,6 +970,36 @@ tablecheck : ICMP { table->conf.check = CHECK_ICMP; translate_string(table->conf.exbuf); free($4); } + | BINARY SEND sendbinbuf EXPECT STRING opttls { + table->conf.check = CHECK_BINSEND_EXPECT; + if ($6) { + conf->sc_conf.flags |= F_TLS; + table->conf.flags |= F_TLS; + } + if (strlen($5) == 0) { + yyerror("empty binary expect data"); + free($5); + YYERROR; + } + if (strlcpy(table->conf.exbuf, $5, + sizeof(table->conf.exbuf)) + >= sizeof(table->conf.exbuf)) { + yyerror("expect buffer truncated"); + free($5); + YYERROR; + } + struct ibuf *ibuf = string2binary($5); + if (ibuf == NULL) { + yyerror("failed in binary expect data buffer"); + ibuf_free(ibuf); + free($5); + YYERROR; + } + memcpy(table->conf.exbinbuf, ibuf->buf, + ibuf_size(ibuf)); + ibuf_free(ibuf); + free($5); + } | SCRIPT STRING { table->conf.check = CHECK_SCRIPT; if (strlcpy(table->conf.path, $2, @@ -2309,6 +2358,7 @@ lookup(char *s) { "append", APPEND }, { "backlog", BACKLOG }, { "backup", BACKUP }, + { "binary", BINARY }, { "block", BLOCK }, { "buffer", BUFFER }, { "ca", CA }, @@ -2890,6 +2940,8 @@ load_config(const char *filename, struct relayd *x_con } if (table->sendbuf != NULL) free(table->sendbuf); + if (table->sendbinbuf != NULL) + ibuf_free(table->sendbinbuf); free(table); continue; } blob - eda2064646c59255b035f06f8c38145714c9b8a4 blob + fc10929359ffa807c52ee579da85f29b6898b2dd --- relayd.c +++ relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.181 2019/08/30 16:54:20 sthen Exp $ */ +/* $OpenBSD: relayd.c,v 1.182 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2007 - 2016 Reyk Floeter @@ -530,6 +530,7 @@ purge_table(struct relayd *env, struct tablelist *head free(host); } free(table->sendbuf); + ibuf_free(table->sendbinbuf); tls_config_free(table->tls_cfg); if (head != NULL) blob - f45585565ed31f50c49ef29422f1405bf5519db9 blob + 5f6b94f12cd6e7e228a4c07cb9a8ecb926c3db2a --- relayd.conf.5 +++ relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.192 2019/07/05 13:42:06 robert Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.193 2019/09/15 19:23:29 rob Exp $ .\" .\" Copyright (c) 2006 - 2016 Reyk Floeter .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 5 2019 $ +.Dd $Mdocdate: September 15 2019 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -372,6 +372,33 @@ If the .Ic tls keyword is present, the transaction will occur in a TLS tunnel. +.It Xo +.Ic check binary send +.Ar data +.Ic expect +.Ar data +.Op Ic tls +.Xc +For each host in the table, a TCP connection is established on the +port specified, then the +.Ic send +.Ar data +is converted into binary and sent. +Incoming (binary) +data is then read and is expected to match against a binary +conversion of the +.Ic expect +.Ar data +using +.Xr memcmp 3 . +.Ar data +must be populated with a string containing an even number of hexadecimal +single-byte characters and must not be empty. +This can be useful with binary protocols such as LDAP and SNMP. +If the +.Ic tls +keyword is present, +the transaction will occur in a TLS tunnel. .It Ic check tcp Use a simple TCP connect to check that hosts are up. .It Ic check tls blob - ba841ddfcd31ea39876dce41501d0cf735387644 blob + d5ddbc143e21617241b84c8a8b896ab4b95fe602 --- relayd.h +++ relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.259 2019/06/26 12:13:47 reyk Exp $ */ +/* $OpenBSD: relayd.h,v 1.260 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter @@ -504,7 +504,8 @@ struct table_config { char name[TABLE_NAME_SIZE]; size_t name_len; char path[PATH_MAX]; - char exbuf[64]; + unsigned char exbinbuf[128]; + char exbuf[256]; char digest[41]; /* length of sha1 digest * 2 */ u_int8_t digest_type; enum forwardmode fwdmode; @@ -517,6 +518,7 @@ struct table { int skipped; struct hostlist hosts; struct tls_config *tls_cfg; + struct ibuf *sendbinbuf; char *sendbuf; }; TAILQ_HEAD(tablelist, table); @@ -527,8 +529,9 @@ enum table_check { CHECK_TCP = 2, CHECK_HTTP_CODE = 3, CHECK_HTTP_DIGEST = 4, - CHECK_SEND_EXPECT = 5, - CHECK_SCRIPT = 6 + CHECK_BINSEND_EXPECT = 5, + CHECK_SEND_EXPECT = 6, + CHECK_SCRIPT = 7 }; struct rdr_config { @@ -1169,6 +1172,9 @@ const char *print_host(struct sockaddr_storage *, char const char *print_time(struct timeval *, struct timeval *, char *, size_t); const char *printb_flags(const u_int32_t, const char *); void getmonotime(struct timeval *); +struct ibuf *string2binary(const char *); +void print_hex(uint8_t *, off_t, size_t); +void print_debug(const char *, ...); /* pfe.c */ void pfe(struct privsep *, struct privsep_proc *); blob - a566a4bc9f6cb952d54fd5afc025739474561d13 blob + 8a06dc4a41977cc9b603ab953215700360c121d2 --- util.c +++ util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.2 2019/05/13 09:54:07 reyk Exp $ */ +/* $OpenBSD: util.c,v 1.3 2019/09/15 19:23:29 rob Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter @@ -168,6 +168,7 @@ table_check(enum table_check check) return ("http code"); case CHECK_HTTP_DIGEST: return ("http digest"); + case CHECK_BINSEND_EXPECT: case CHECK_SEND_EXPECT: return ("send expect"); case CHECK_SCRIPT: @@ -284,3 +285,78 @@ getmonotime(struct timeval *tv) TIMESPEC_TO_TIMEVAL(tv, &ts); } + +struct ibuf * +string2binary(const char *string) +{ + unsigned long i, j, x; + unsigned char *binary = NULL; + struct ibuf *ibuf = NULL; + char hex[3]; + int len; + + if (strlen(string) % 2 != 0) { + return NULL; + } + + binary = calloc(strlen(string), sizeof(unsigned char)); + if (binary == NULL) { + return NULL; + } + + hex[2] = '\0'; + j = 0; + for (i = 0; i < strlen(string); i++) { + if (isxdigit(string[i]) == 0 || isxdigit(string[i+1]) == 0) { + free(binary); + return NULL; + } else { + hex[0] = string[i]; + hex[1] = string[i+1]; + x = strtoul(hex, NULL, 16); + binary[j++] = (unsigned char)x; + i++; + } + } + len = strlen(string) / 2; + if ((ibuf = ibuf_open(len)) == NULL || + ibuf_add(ibuf, binary, len) == -1) { + ibuf_free(ibuf); + free(binary); + return NULL; + } + free(binary); + return ibuf; +} + +void +print_hex(uint8_t *buf, off_t offset, size_t length) +{ + unsigned int i; + + if (log_getverbose() < 3 || !length) + return; + + for (i = 0; i < length; i++) { + if (i && (i % 4) == 0) { + if ((i % 32) == 0) + print_debug("\n"); + else + print_debug(" "); + } + print_debug("%02x", buf[offset + i]); + } + print_debug("\n"); +} + +void +print_debug(const char *emsg, ...) +{ + va_list ap; + + if (log_getverbose() > 2) { + va_start(ap, emsg); + vfprintf(stderr, emsg, ap); + va_end(ap); + } +}