diff --git docs/manpages/man5/protocols.cfg.5.html docs/manpages/man5/protocols.cfg.5.html index adba064d..0e381503 100644 --- docs/manpages/man5/protocols.cfg.5.html +++ docs/manpages/man5/protocols.cfg.5.html @@ -55,6 +55,21 @@ first established, xymonnet will send the string "mail\r\nquit\r\n" to the service. It will then expect a response beginning with "220". Any data returned by the service (a so-called "banner") will be recorded and included in the status message. +

+For services that require ALPN protocol negotiation (such as IMAPS multiplexed +by Nginx on the same port as HTTPS), you can define: +
+ +

+
   [imaps443] +
+ +
      options ssl,alpn=imap +
+ +
      port 443 +
+

The full set of commands available for the protocols.cfg file are:

@@ -100,6 +115,9 @@ Defines test options. The possible options are

   telnet - service is telnet, so exchange telnet options +
+ +
   alpn=protocol1,protocol2 - specify ALPN protocols to negotiate during SSL handshake

diff --git lib/netservices.c lib/netservices.c index 2e1845ae..c2689c03 100644 --- lib/netservices.c +++ lib/netservices.c @@ -151,6 +151,7 @@ char *init_tcp_services(void) if (svcinfo[i].svcname) xfree(svcinfo[i].svcname); if (svcinfo[i].sendtxt) xfree(svcinfo[i].sendtxt); if (svcinfo[i].exptext) xfree(svcinfo[i].exptext); + if (svcinfo[i].alpns) xfree(svcinfo[i].alpns); } xfree(svcinfo); svcinfo = default_svcinfo; @@ -243,6 +244,9 @@ char *init_tcp_services(void) if (strcmp(opt, "ssl") == 0) first->rec->flags |= TCP_SSL; else if (strcmp(opt, "banner") == 0) first->rec->flags |= TCP_GET_BANNER; else if (strcmp(opt, "telnet") == 0) first->rec->flags |= TCP_TELNET; + else if (strncmp(opt, "alpn=", 5) == 0) { + first->rec->alpns = strdup(opt+5); + } else errprintf("Unknown option: %s\n", opt); opt = strtok(NULL, ","); @@ -276,6 +280,7 @@ char *init_tcp_services(void) svcinfo[i].expofs = walk->rec->expofs; svcinfo[i].flags = walk->rec->flags; svcinfo[i].port = walk->rec->port; + svcinfo[i].alpns = walk->rec->alpns; } memset(&svcinfo[svccount], 0, sizeof(svcinfo_t)); diff --git lib/netservices.h lib/netservices.h index 5bbfb089..82819286 100644 --- lib/netservices.h +++ lib/netservices.h @@ -18,6 +18,7 @@ #define TCP_TELNET 0x0002 #define TCP_SSL 0x0004 #define TCP_HTTP 0x0008 +#define TCP_ALPN 0x0010 typedef struct svcinfo_t { char *svcname; @@ -27,6 +28,7 @@ typedef struct svcinfo_t { int expofs, explen; unsigned int flags; int port; + char *alpns; /* ALPN protocols (comma-separated) */ } svcinfo_t; extern char *init_tcp_services(void); diff --git xymonnet/contest.c xymonnet/contest.c index 7f089a2d..10036f3a 100644 --- xymonnet/contest.c +++ xymonnet/contest.c @@ -70,7 +70,7 @@ int snienabled = 0; /* SNI disabled by default */ static svcinfo_t svcinfo_http = { "http", NULL, 0, NULL, 0, 0, (TCP_GET_BANNER|TCP_HTTP), 80 }; static svcinfo_t svcinfo_https = { "https", NULL, 0, NULL, 0, 0, (TCP_GET_BANNER|TCP_HTTP|TCP_SSL), 443 }; -static ssloptions_t default_sslopt = { NULL, SSLVERSION_DEFAULT, NULL }; +static ssloptions_t default_sslopt = { NULL, SSLVERSION_DEFAULT, NULL, NULL }; static time_t sslcert_expiretime(char *timestr) { @@ -210,7 +210,8 @@ tcptest_t *add_tcp_test(char *ip, int port, char *service, ssloptions_t *sslopt, newtest->certinfo = NULL; newtest->certissuer = NULL; newtest->certexpires = 0; - newtest->sslrunning = ((newtest->svcinfo->flags & TCP_SSL) ? SSLSETUP_PENDING : 0); + /* If ALPN is configured, SSL is also necessarily enabled */ + newtest->sslrunning = (((newtest->svcinfo->flags & TCP_SSL) || (newtest->svcinfo->flags & TCP_ALPN)) ? SSLSETUP_PENDING : 0); newtest->sslagain = 0; newtest->banner = NULL; @@ -555,6 +556,49 @@ static void setup_ssl(tcptest_t *item) if (item->ssloptions->cipherlist) SSL_CTX_set_cipher_list(item->sslctx, item->ssloptions->cipherlist); + /* Set ALPN protocols if specified */ + /* First check service definition for ALPN, then fallback to ssloptions */ + const char *alpn_protocols = NULL; + if (item->svcinfo && item->svcinfo->alpns) { + alpn_protocols = item->svcinfo->alpns; + } else if (item->ssloptions && item->ssloptions->alpns) { + alpn_protocols = item->ssloptions->alpns; + } + + if (alpn_protocols) { + /* Create ALPN protocol list in wire format */ + char *alpn_copy = strdup(alpn_protocols); + char *ptr, *token, *saveptr; + unsigned char alpn_buffer[256]; /* Buffer for ALPN wire format */ + int offset = 0; + + /* Parse comma-separated protocol list */ + ptr = alpn_copy; + while ((token = strtok_r(ptr, ",", &saveptr)) != NULL) { + ptr = NULL; /* For subsequent strtok_r calls */ + + /* Remove leading/trailing whitespace */ + while (*token == ' ' || *token == '\t') token++; + int len = strlen(token); + while (len > 0 && (token[len-1] == ' ' || token[len-1] == '\t')) { + token[len-1] = '\0'; + len--; + } + + if (len > 0 && (offset + len + 1) < sizeof(alpn_buffer)) { + alpn_buffer[offset] = len; /* Length byte */ + memcpy(&alpn_buffer[offset + 1], token, len); /* Protocol name */ + offset += len + 1; + } + } + + if (offset > 0) { + SSL_CTX_set_alpn_protos(item->sslctx, alpn_buffer, offset); + } + + free(alpn_copy); + } + if (item->ssloptions->clientcert) { int status; char certfn[PATH_MAX]; diff --git xymonnet/contest.h xymonnet/contest.h index 5d489d29..8a616ffe 100644 --- xymonnet/contest.h +++ xymonnet/contest.h @@ -68,6 +68,7 @@ typedef struct { char *cipherlist; int sslversion; char *clientcert; + char *alpns; /* ALPN protocols (comma-separated) */ } ssloptions_t; typedef int (*f_callback_data)(unsigned char *buffer, unsigned int size, void *privdata); diff --git xymonnet/protocols.cfg.5 xymonnet/protocols.cfg.5 index 4ee6caf8..d122e4d3 100644 --- xymonnet/protocols.cfg.5 +++ xymonnet/protocols.cfg.5 @@ -35,6 +35,17 @@ to the service. It will then expect a response beginning with "220". Any data returned by the service (a so-called "banner") will be recorded and included in the status message. .sp +For services that require ALPN protocol negotiation (such as IMAPS multiplexed +by Nginx on the same port as HTTPS), you can define: +.br +.sp + [imaps443] +.br + options ssl,alpn=imap +.br + port 443 +.br +.sp The full set of commands available for the protocols.cfg file are: .IP "[NAME]" @@ -73,6 +84,8 @@ Defines test options. The possible options are ssl - service uses SSL so perform an SSL handshake .br telnet - service is telnet, so exchange telnet options +.br + alpn=protocol1,protocol2 - specify ALPN protocols to negotiate during SSL handshake .SH FILES