+ rv = ssl3_ValidateNextProtoNego(data->data, data->len);
+ if (rv != SECSuccess)
+ return rv;
+
+ PORT_Assert(ss->nextProtoCallback);
+ rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
+ result.data, &result.len, sizeof resultBuffer);
+ if (rv != SECSuccess)
+ return rv;
+ /* If the callback wrote more than allowed to |result| it has corrupted our
+ * stack. */
+ if (result.len > sizeof resultBuffer) {
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ if (ex_type == ssl_app_layer_protocol_xtn &&
+ ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) {
+ /* The callback might say OK, but then it's picked a default.
+ * That's OK for NPN, but not ALPN. */
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL);
+ (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol);
+ return SECFailure;
+ }
+
+ ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+
+ SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+ return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
+}
+
+/* handle an incoming ALPN extension at the server */
+static SECStatus
+ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
+{
+ int count;
+ SECStatus rv;
+
+ /* We expressly don't want to allow ALPN on renegotiation,
+ * despite it being permitted by the spec. */
+ if (ss->firstHsDone || data->len == 0) {
+ /* Clients MUST send a non-empty ALPN extension. */
+ PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+ return SECFailure;
+ }
+
+ /* unlike NPN, ALPN has extra redundant length information so that
+ * the extension is the same in both ClientHello and ServerHello */
+ count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+ if (count < 0) {
+ return SECFailure; /* fatal alert was sent */
+ }
+ if (count != data->len) {
+ return ssl3_DecodeError(ss);
+ }
+
+ if (!ss->nextProtoCallback) {
+ /* we're not configured for it */
+ return SECSuccess;
+ }
+
+ rv = ssl3_SelectAppProtocol(ss, ex_type, data);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ /* prepare to send back a response, if we negotiated */
+ if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) {
+ return ssl3_RegisterServerHelloExtensionSender(
+ ss, ex_type, ssl3_ServerSendAppProtoXtn);
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
+ SECItem *data)
+{