+#ifndef OPENSSL_NO_NEXTPROTONEG
+/* Parse the comma-separated list into TLS format. */
+static void parse_protos(const char *protos, unsigned char **out, size_t *outlen)
+{
+ size_t len, i, prefix;
+
+ len = strlen(protos);
+
+ /* Should never have reuse. */
+ OPENSSL_assert(*out == NULL);
+
+ /* Test values are small, so we omit length limit checks. */
+ *out = OPENSSL_malloc(len + 1);
+ OPENSSL_assert(*out != NULL);
+ *outlen = len + 1;
+
+ /*
+ * foo => '3', 'f', 'o', 'o'
+ * foo,bar => '3', 'f', 'o', 'o', '3', 'b', 'a', 'r'
+ */
+ memcpy(*out + 1, protos, len);
+
+ prefix = 0;
+ i = prefix + 1;
+ while (i <= len) {
+ if ((*out)[i] == ',') {
+ OPENSSL_assert(i - 1 - prefix > 0);
+ (*out)[prefix] = i - 1 - prefix;
+ prefix = i;
+ }
+ i++;
+ }
+ OPENSSL_assert(len - prefix > 0);
+ (*out)[prefix] = len - prefix;
+}
+
+/*
+ * The client SHOULD select the first protocol advertised by the server that it
+ * also supports. In the event that the client doesn't support any of server's
+ * protocols, or the server doesn't advertise any, it SHOULD select the first
+ * protocol that it supports.
+ */
+static int client_npn_cb(SSL *s, unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ void *arg)
+{
+ CTX_DATA *ctx_data = (CTX_DATA*)(arg);
+ int ret;
+
+ ret = SSL_select_next_proto(out, outlen, in, inlen,
+ ctx_data->npn_protocols,
+ ctx_data->npn_protocols_len);
+ /* Accept both OPENSSL_NPN_NEGOTIATED and OPENSSL_NPN_NO_OVERLAP. */
+ OPENSSL_assert(ret == OPENSSL_NPN_NEGOTIATED
+ || ret == OPENSSL_NPN_NO_OVERLAP);
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static int server_npn_cb(SSL *s, const unsigned char **data,
+ unsigned int *len, void *arg)
+{
+ CTX_DATA *ctx_data = (CTX_DATA*)(arg);
+ *data = ctx_data->npn_protocols;
+ *len = ctx_data->npn_protocols_len;
+ return SSL_TLSEXT_ERR_OK;
+}
+
+/*
+ * The server SHOULD select the most highly preferred protocol that it supports
+ * and that is also advertised by the client. In the event that the server
+ * supports no protocols that the client advertises, then the server SHALL
+ * respond with a fatal "no_application_protocol" alert.
+ */
+static int server_alpn_cb(SSL *s, const unsigned char **out,
+ unsigned char *outlen, const unsigned char *in,
+ unsigned int inlen, void *arg)
+{
+ CTX_DATA *ctx_data = (CTX_DATA*)(arg);
+ int ret;
+
+ /* SSL_select_next_proto isn't const-correct... */
+ unsigned char *tmp_out;
+
+ /*
+ * The result points either to |in| or to |ctx_data->alpn_protocols|.
+ * The callback is allowed to point to |in| or to a long-lived buffer,
+ * so we can return directly without storing a copy.
+ */
+ ret = SSL_select_next_proto(&tmp_out, outlen,
+ ctx_data->alpn_protocols,
+ ctx_data->alpn_protocols_len, in, inlen);
+
+ *out = tmp_out;
+ /* Unlike NPN, we don't tolerate a mismatch. */
+ return ret == OPENSSL_NPN_NEGOTIATED ? SSL_TLSEXT_ERR_OK
+ : SSL_TLSEXT_ERR_NOACK;
+}
+#endif
+