diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-07-26 10:33:24 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-07-26 14:23:55 +0200 |
commit | 7e051ae28c288c218584f75dbc6c097a3b2564c9 (patch) | |
tree | 9456e41b15aac3333770d58715c7487607318af3 /src | |
parent | e9b7e84870bc1bfea4969a57d15e523133b46ecb (diff) | |
download | gnutls-7e051ae28c288c218584f75dbc6c097a3b2564c9.tar.gz |
tools: TLS handling has been incorporated into socket_open()
This is of particular usage to the server IP address loop, since
we can detect fast open errors and retry handshake to the next IP
address.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/cli-debug.c | 35 | ||||
-rw-r--r-- | src/cli.c | 58 | ||||
-rw-r--r-- | src/common.c | 22 | ||||
-rw-r--r-- | src/common.h | 2 | ||||
-rw-r--r-- | src/danetool.c | 70 | ||||
-rw-r--r-- | src/ocsptool-common.c | 3 | ||||
-rw-r--r-- | src/ocsptool.c | 10 | ||||
-rw-r--r-- | src/socket.c | 88 | ||||
-rw-r--r-- | src/socket.h | 19 |
10 files changed, 185 insertions, 125 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 37a9bea71e..182f3a5035 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -96,7 +96,7 @@ libcmd_psk_la_SOURCES = psktool-args.def psktool-args.c psktool-args.h if ENABLE_OCSP ocsptool_SOURCES = ocsptool.c ocsptool-common.h ocsptool-common.c \ - socket.c socket.h + socket.c common.c common.h socket.h ocsptool_LDADD = ../lib/libgnutls.la libcmd-ocsp.la $(LIBOPTS) ../gl/libgnu.la ocsptool_LDADD += $(LTLIBINTL) $(GETADDRINFO_LIB) gl/libgnu_gpl.la $(LIBIDN_LIBS) noinst_LTLIBRARIES += libcmd-ocsp.la @@ -105,7 +105,6 @@ libcmd_ocsp_la_SOURCES = ocsptool-args.def ocsptool-args.h ocsptool-args.c gnutls_serv_SOURCES = \ list.h serv.c \ udp-serv.c udp-serv.h \ - socket.c socket.h \ common.h common.c \ certtool-common.h gnutls_serv_LDADD = ../lib/libgnutls.la diff --git a/src/cli-debug.c b/src/cli-debug.c index 22020eb818..3323b4804e 100644 --- a/src/cli-debug.c +++ b/src/cli-debug.c @@ -188,11 +188,28 @@ static const TLS_TEST tls_tests[] = { const char *ip; +gnutls_session_t init_tls_session(const char *host) +{ + gnutls_session_t state = NULL; + gnutls_init(&state, GNUTLS_CLIENT | GNUTLS_NO_EXTENSIONS); + + set_read_funcs(state); + if (host && is_ip(host) == 0) + gnutls_server_name_set(state, GNUTLS_NAME_DNS, + host, strlen(host)); + + return state; +} + +int do_handshake(socket_st * socket) +{ + return 0; /* we do it locally */ +} + int main(int argc, char **argv) { int ret; int i; - gnutls_session_t state; char portname[6]; socket_st hd; char app_proto[32] = ""; @@ -268,26 +285,16 @@ int main(int argc, char **argv) break; } - socket_open(&hd, hostname, portname, 0, NULL); + socket_open(&hd, hostname, portname, app_proto, SOCKET_FLAG_STARTTLS, NULL, NULL); hd.verbose = verbose; - socket_starttls(&hd, app_proto); - - gnutls_init(&state, GNUTLS_CLIENT | GNUTLS_NO_EXTENSIONS); - - gnutls_transport_set_int(state, hd.fd); - set_read_funcs(state); - if (hostname && is_ip(hostname) == 0) - gnutls_server_name_set(state, GNUTLS_NAME_DNS, - hostname, strlen(hostname)); - do { if (strcmp(app_proto, "https") != 0 && tls_tests[i].https_only != 0) { i++; break; } - ret = tls_tests[i].func(state); + ret = tls_tests[i].func(hd.session); if (ret != TEST_IGNORE) { printf("%58s...", tls_tests[i].test_name); @@ -313,8 +320,6 @@ int main(int argc, char **argv) while (ret == TEST_IGNORE && tls_tests[i].test_name != NULL); - gnutls_deinit(state); - socket_bye(&hd); i++; @@ -121,7 +121,6 @@ static gnutls_certificate_credentials_t xcred; /* prototypes */ static void check_rehandshake(socket_st * socket, int ret); -static int do_handshake(socket_st * socket); static void init_global_tls_stuff(void); static int cert_verify_ocsp(gnutls_session_t session); @@ -660,7 +659,7 @@ cert_callback(gnutls_session_t session, /* initializes a gnutls_session_t with some defaults. */ -static gnutls_session_t init_tls_session(const char *host) +gnutls_session_t init_tls_session(const char *host) { const char *err; int ret; @@ -892,7 +891,7 @@ static int try_rehandshake(socket_st * hd) static int try_resume(socket_st * hd) { - int ret; + int ret, socket_flags = 0; gnutls_datum_t rdata = {NULL, 0}; if (gnutls_session_is_resumed(hd->session) == 0) { @@ -915,25 +914,15 @@ static int try_resume(socket_st * hd) printf ("\n\n- Connecting again- trying to resume previous session\n"); - socket_open(hd, hostname, service, udp | (fastopen << 1), CONNECT_MSG); - if (HAVE_OPT(STARTTLS_PROTO)) - socket_starttls(hd, OPT_ARG(STARTTLS_PROTO)); + socket_flags |= SOCKET_FLAG_STARTTLS; + else if (fastopen) + socket_flags |= SOCKET_FLAG_FASTOPEN; - hd->session = init_tls_session(hostname); - if (rdata.data) { - hd->rdata.data = rdata.data; - hd->rdata.size = rdata.size; + if (udp) + socket_flags |= SOCKET_FLAG_UDP; - gnutls_session_set_data(hd->session, hd->rdata.data, hd->rdata.size); - } - - ret = do_handshake(hd); - if (ret < 0) { - fprintf(stderr, "*** Resume handshake has failed\n"); - gnutls_perror(ret); - return ret; - } + socket_open(hd, hostname, service, OPT_ARG(STARTTLS_PROTO), socket_flags, CONNECT_MSG, &rdata); printf("- Resume Handshake was completed\n"); if (gnutls_session_is_resumed(hd->session) != 0) @@ -1193,6 +1182,7 @@ int main(int argc, char **argv) char *keyboard_buffer_ptr; inline_cmds_st inline_cmds; unsigned last_op_is_write = 0; + int socket_flags = 0; #ifndef _WIN32 struct sigaction new_action; #endif @@ -1218,26 +1208,15 @@ int main(int argc, char **argv) canonicalize_host(hostname, service, sizeof(service)); - socket_open(&hd, hostname, service, udp | (fastopen << 1), CONNECT_MSG); - hd.verbose = verbose; - - if (HAVE_OPT(STARTTLS_PROTO)) - socket_starttls(&hd, OPT_ARG(STARTTLS_PROTO)); - - hd.session = init_tls_session(hostname); + if (udp) + socket_flags |= SOCKET_FLAG_UDP; + if (fastopen) + socket_flags |= SOCKET_FLAG_FASTOPEN; if (starttls) - goto after_handshake; - - ret = do_handshake(&hd); + socket_flags |= SOCKET_FLAG_STARTTLS; - if (ret < 0) { - fprintf(stderr, "*** Handshake has failed\n"); - gnutls_perror(ret); - print_other_info(hd.session); - gnutls_deinit(hd.session); - return 1; - } else - printf("- Handshake was completed\n"); + socket_open(&hd, hostname, service, OPT_ARG(STARTTLS_PROTO), socket_flags, CONNECT_MSG, NULL); + hd.verbose = verbose; if (resume != 0) if (try_resume(&hd)) @@ -1245,8 +1224,6 @@ int main(int argc, char **argv) print_other_info(hd.session); - after_handshake: - /* Warning! Do not touch this text string, it is used by external programs to search for when gnutls-cli has reached this point. */ printf("\n- Simple Client Mode:\n\n"); @@ -1670,7 +1647,7 @@ static void check_rehandshake(socket_st * socket, int ret) } -static int do_handshake(socket_st * socket) +int do_handshake(socket_st * socket) { int ret; @@ -1680,7 +1657,6 @@ static int do_handshake(socket_st * socket) socket->connect_addrlen); socket->connect_addrlen = 0; } else { - gnutls_transport_set_int(socket->session, socket->fd); set_read_funcs(socket->session); } diff --git a/src/common.c b/src/common.c index 066022f00d..15376d4b05 100644 --- a/src/common.c +++ b/src/common.c @@ -37,6 +37,13 @@ #include <gnutls/crypto.h> #include <time.h> #include <common.h> +#include <unistd.h> + +#ifndef _WIN32 +# include <signal.h> +#else +#include <ws2tcpip.h> +#endif #ifdef ENABLE_PKCS11 #include <gnutls/pkcs11.h> @@ -1124,3 +1131,18 @@ void pkcs11_common(common_info_st *c) } #endif + +void sockets_init(void) +{ +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(1, 1); + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + perror("WSA_STARTUP_ERROR"); + } +#else + signal(SIGPIPE, SIG_IGN); +#endif +} diff --git a/src/common.h b/src/common.h index 7c14434b95..8cd321e040 100644 --- a/src/common.h +++ b/src/common.h @@ -87,6 +87,8 @@ int len = strlen(hostname); return 0; } +void sockets_init(void); + #ifdef _WIN32 static int system_recv_timeout(gnutls_transport_ptr_t ptr, unsigned int ms) { diff --git a/src/danetool.c b/src/danetool.c index bdccbb3c8b..4c35964178 100644 --- a/src/danetool.c +++ b/src/danetool.c @@ -614,23 +614,18 @@ static int cert_callback(gnutls_session_t session) return -1; } -static int get_cert(socket_st *hd, const char *hostname, unsigned udp, int fd) +static gnutls_certificate_credentials_t xcred; +static int file_fd = -1; +static unsigned udp = 0; + +gnutls_session_t init_tls_session(const char *hostname) { - gnutls_certificate_credentials_t xcred; gnutls_session_t session; int ret; - struct priv_st priv; + static struct priv_st priv; priv.found = 0; - priv.fd = fd; - - ret = gnutls_certificate_allocate_credentials(&xcred); - if (ret < 0) { - fprintf(stderr, "error[%d]: %s\n", __LINE__, - gnutls_strerror(ret)); - exit(1); - } - gnutls_certificate_set_verify_function(xcred, cert_callback); + priv.fd = file_fd; ret = gnutls_init(&session, (udp?GNUTLS_DATAGRAM:0)|GNUTLS_CLIENT); if (ret < 0) { @@ -639,7 +634,6 @@ static int get_cert(socket_st *hd, const char *hostname, unsigned udp, int fd) exit(1); } gnutls_session_set_ptr(session, &priv); - gnutls_transport_set_int(session, hd->fd); gnutls_set_default_priority(session); if (hostname && is_ip(hostname)==0) { @@ -647,29 +641,40 @@ static int get_cert(socket_st *hd, const char *hostname, unsigned udp, int fd) } gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); + return session; +} + +int do_handshake(socket_st * socket) +{ + int ret; + do { - ret = gnutls_handshake(session); + ret = gnutls_handshake(socket->session); } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_WARNING_ALERT_RECEIVED); /* we don't care on the result */ - gnutls_deinit(session); - gnutls_certificate_free_credentials(xcred); - - if (priv.found == 0) - return -1; - return 0; } + static const char *obtain_cert(const char *hostname, const char *proto, const char *service, const char *app_proto, unsigned quiet) { socket_st hd; const char *txt_service; - unsigned udp = 0; static char tmpfile[32]; - int fd, ret; + int ret; const char *str = "Obtaining certificate from"; + int socket_flags = 0; + struct priv_st *priv; + + ret = gnutls_certificate_allocate_credentials(&xcred); + if (ret < 0) { + fprintf(stderr, "error[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); + exit(1); + } + gnutls_certificate_set_verify_function(xcred, cert_callback); if (strcmp(proto, "udp") == 0) udp = 1; @@ -685,24 +690,33 @@ static const char *obtain_cert(const char *hostname, const char *proto, const ch if (quiet) str = NULL; - socket_open(&hd, hostname, txt_service, udp, str); if (app_proto == NULL) app_proto = txt_service; - socket_starttls(&hd, app_proto); + + if (udp) + socket_flags |= SOCKET_FLAG_UDP; + umask(066); - fd = mkstemp(tmpfile); - if (fd == -1) { + file_fd = mkstemp(tmpfile); + if (file_fd == -1) { int e = errno; fprintf(stderr, "error[%d]: %s\n", __LINE__, strerror(e)); exit(1); } - ret = get_cert(&hd, hostname, udp, fd); - close(fd); + socket_open(&hd, hostname, txt_service, app_proto, socket_flags|SOCKET_FLAG_STARTTLS, str, NULL); + + close(file_fd); + + ret = 0; + priv = gnutls_session_get_ptr(hd.session); + if (priv->found == 0) + ret = -1; socket_bye(&hd); + gnutls_certificate_free_credentials(xcred); if (ret == -1) return NULL; diff --git a/src/ocsptool-common.c b/src/ocsptool-common.c index beab82b27a..5ab753cf84 100644 --- a/src/ocsptool-common.c +++ b/src/ocsptool-common.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include "common.h" #include <gnutls/gnutls.h> #include <gnutls/ocsp.h> @@ -204,7 +205,7 @@ int send_ocsp_request(const char *server, (unsigned int) req.size); headers_size = strlen(headers); - socket_open(&hd, hostname, service, 0, CONNECT_MSG); + socket_open(&hd, hostname, service, NULL, SOCKET_FLAG_RAW, CONNECT_MSG, NULL); socket_send(&hd, headers, headers_size); socket_send(&hd, req.data, req.size); diff --git a/src/ocsptool.c b/src/ocsptool.c index 5ada2003c2..9493a8d436 100644 --- a/src/ocsptool.c +++ b/src/ocsptool.c @@ -47,6 +47,16 @@ static void tls_log_func(int level, const char *str) fprintf(stderr, "|<%d>| %s", level, str); } +gnutls_session_t init_tls_session(const char *host) +{ + return NULL; +} + +int do_handshake(socket_st * socket) +{ + return -1; +} + static void request_info(void) { gnutls_ocsp_req_t req; diff --git a/src/socket.c b/src/socket.c index 35fd61534e..aae9ee7403 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * Copyright (C) 2000-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. * * This file is part of GnuTLS. * @@ -201,18 +202,18 @@ ssize_t wait_for_text(socket_st * socket, const char *txt, unsigned txt_size) return ret; } -void -socket_starttls(socket_st * socket, const char *app_proto) +static void +socket_starttls(socket_st * socket) { char buf[512]; if (socket->secure) return; - if (app_proto == NULL || strcasecmp(app_proto, "https") == 0) + if (socket->app_proto == NULL || strcasecmp(socket->app_proto, "https") == 0) return; - if (strcasecmp(app_proto, "smtp") == 0 || strcasecmp(app_proto, "submission") == 0) { + if (strcasecmp(socket->app_proto, "smtp") == 0 || strcasecmp(socket->app_proto, "submission") == 0) { if (socket->verbose) printf("Negotiating SMTP STARTTLS\n"); @@ -222,7 +223,7 @@ socket_starttls(socket_st * socket, const char *app_proto) wait_for_text(socket, "250 ", 4); send_line(socket, "STARTTLS\n"); wait_for_text(socket, "220 ", 4); - } else if (strcasecmp(app_proto, "imap") == 0 || strcasecmp(app_proto, "imap2") == 0) { + } else if (strcasecmp(socket->app_proto, "imap") == 0 || strcasecmp(socket->app_proto, "imap2") == 0) { if (socket->verbose) printf("Negotiating IMAP STARTTLS\n"); @@ -230,7 +231,7 @@ socket_starttls(socket_st * socket, const char *app_proto) wait_for_text(socket, "a OK", 4); send_line(socket, "a STARTTLS\r\n"); wait_for_text(socket, "a OK", 4); - } else if (strcasecmp(app_proto, "xmpp") == 0) { + } else if (strcasecmp(socket->app_proto, "xmpp") == 0) { if (socket->verbose) printf("Negotiating XMPP STARTTLS\n"); @@ -239,13 +240,13 @@ socket_starttls(socket_st * socket, const char *app_proto) wait_for_text(socket, "<?", 2); send_line(socket, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); wait_for_text(socket, "<proceed", 8); - } else if (strcasecmp(app_proto, "ldap") == 0) { + } else if (strcasecmp(socket->app_proto, "ldap") == 0) { if (socket->verbose) printf("Negotiating LDAP STARTTLS\n"); #define LDAP_STR "\x30\x1d\x02\x01\x01\x77\x18\x80\x16\x31\x2e\x33\x2e\x36\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x34\x36\x36\x2e\x32\x30\x30\x33\x37" send(socket->fd, LDAP_STR, sizeof(LDAP_STR)-1, 0); wait_for_text(socket, NULL, 0); - } else if (strcasecmp(app_proto, "ftp") == 0 || strcasecmp(app_proto, "ftps") == 0) { + } else if (strcasecmp(socket->app_proto, "ftp") == 0 || strcasecmp(socket->app_proto, "ftps") == 0) { if (socket->verbose) printf("Negotiating FTP STARTTLS\n"); @@ -254,10 +255,10 @@ socket_starttls(socket_st * socket, const char *app_proto) send_line(socket, "AUTH TLS\r\n"); wait_for_text(socket, "234", 3); } else { - if (!c_isdigit(app_proto[0])) { + if (!c_isdigit(socket->app_proto[0])) { static int warned = 0; if (warned == 0) { - fprintf(stderr, "unknown protocol '%s'\n", app_proto); + fprintf(stderr, "unknown protocol '%s'\n", socket->app_proto); warned = 1; } } @@ -309,7 +310,7 @@ void socket_bye(socket_st * socket) ret = gnutls_bye(socket->session, GNUTLS_SHUT_WR); while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); - if (ret < 0) + if (socket->verbose && ret < 0) fprintf(stderr, "*** gnutls_bye() error: %s\n", gnutls_strerror(ret)); gnutls_deinit(socket->session); @@ -352,20 +353,26 @@ void canonicalize_host(char *hostname, char *service, unsigned service_size) snprintf(service, service_size, "%s", p+1); } + void socket_open(socket_st * hd, const char *hostname, const char *service, - int flags, const char *msg) + const char *app_proto, int flags, const char *msg, gnutls_datum_t *rdata) { struct addrinfo hints, *res, *ptr; int sd, err = 0; - int udp = flags & 1; - int fastopen = flags & 2; + int udp = flags & SOCKET_FLAG_UDP; + int fastopen = flags & SOCKET_FLAG_FASTOPEN; char buffer[MAX_BUF + 1]; char portname[16] = { 0 }; char *a_hostname = (char*)hostname; memset(hd, 0, sizeof(*hd)); + if (rdata) { + hd->rdata.data = rdata->data; + hd->rdata.size = rdata->size; + } + #ifdef HAVE_LIBIDN err = idna_to_ascii_8z(hostname, &a_hostname, IDNA_ALLOW_UNASSIGNED); if (err != IDNA_SUCCESS) { @@ -374,6 +381,7 @@ socket_open(socket_st * hd, const char *hostname, const char *service, exit(1); } #endif + hd->hostname = strdup(hostname); if (msg != NULL) printf("Resolving '%s:%s'...\n", a_hostname,service); @@ -426,6 +434,7 @@ socket_open(socket_st * hd, const char *hostname, const char *service, if (msg) printf("%s '%s:%s' (TFO)...\n", msg, buffer, portname); + } else { if (msg) printf("%s '%s:%s'...\n", msg, buffer, portname); @@ -434,6 +443,32 @@ socket_open(socket_st * hd, const char *hostname, const char *service, continue; } + if (!(flags & SOCKET_FLAG_RAW)) { + if (flags & SOCKET_FLAG_STARTTLS) { + socket_starttls(hd); + } + + hd->session = init_tls_session(hostname); + if (hd->rdata.data) { + gnutls_session_set_data(hd->session, hd->rdata.data, hd->rdata.size); + } + + hd->fd = sd; + gnutls_transport_set_int(hd->session, sd); + + err = do_handshake(hd); + if (err == GNUTLS_E_PUSH_ERROR) { /* failed connecting */ + gnutls_deinit(hd->session); + hd->session = NULL; + continue; + } + else if (err < 0) { + fprintf(stderr, "*** handshake has failed: %s\n", gnutls_strerror(err)); + exit(1); + } else if (hd->verbose) + printf("- Handshake was completed\n"); + } + break; } @@ -449,9 +484,12 @@ socket_open(socket_st * hd, const char *hostname, const char *service, exit(1); } - hd->secure = 0; + if (flags & SOCKET_FLAG_RAW) + hd->secure = 0; + else + hd->secure = 1; + hd->fd = sd; - hd->hostname = strdup(hostname); hd->ip = strdup(buffer); hd->service = strdup(portname); hd->ptr = ptr; @@ -463,22 +501,6 @@ socket_open(socket_st * hd, const char *hostname, const char *service, return; } -void sockets_init(void) -{ -#ifdef _WIN32 - WORD wVersionRequested; - WSADATA wsaData; - - wVersionRequested = MAKEWORD(1, 1); - if (WSAStartup(wVersionRequested, &wsaData) != 0) { - perror("WSA_STARTUP_ERROR"); - } -#else - signal(SIGPIPE, SIG_IGN); -#endif - -} - /* converts a textual service or port to * a service. */ diff --git a/src/socket.h b/src/socket.h index 1f1394f812..55e7230300 100644 --- a/src/socket.h +++ b/src/socket.h @@ -1,11 +1,18 @@ #include <gnutls/gnutls.h> #include <gnutls/socket.h> +#define SOCKET_FLAG_UDP 1 +#define SOCKET_FLAG_FASTOPEN (1<<1) +#define SOCKET_FLAG_STARTTLS (1<<2) +#define SOCKET_FLAG_RAW (1<<3) /* unencrypted */ + + typedef struct { int fd; gnutls_session_t session; int secure; char *hostname; + const char *app_proto; char *ip; char *service; struct addrinfo *ptr; @@ -20,6 +27,10 @@ typedef struct { gnutls_datum_t rdata; } socket_st; +/* calling program must provide that */ +extern gnutls_session_t init_tls_session(const char *host); +extern int do_handshake(socket_st * socket); + ssize_t socket_recv(const socket_st * socket, void *buffer, int buffer_size); ssize_t socket_recv_timeout(const socket_st * socket, void *buffer, @@ -28,14 +39,12 @@ ssize_t socket_send(const socket_st * socket, const void *buffer, int buffer_size); ssize_t socket_send_range(const socket_st * socket, const void *buffer, int buffer_size, gnutls_range_st * range); -void socket_open(socket_st * hd, const char *hostname, const char *service, - int flags, const char *msg); +void +socket_open(socket_st * hd, const char *hostname, const char *service, + const char *app_proto, int flags, const char *msg, gnutls_datum_t *rdata); -void socket_starttls(socket_st * hd, const char *app_proto); void socket_bye(socket_st * socket); -void sockets_init(void); - int service_to_port(const char *service, const char *proto); const char *port_to_service(const char *sport, const char *proto); int starttls_proto_to_port(const char *app_proto); |