@@ -119,6 +119,7 @@ into.
119119 3. [Running](#run-ecc)
120120
1211216. [Encrypted Client Hello](#ech)
122+ 7. [ALPN](#alpn)
122123
123124
124125
@@ -1591,6 +1592,126 @@ Expected behavior:
15911592- The server logs the received early data and replies both during early-data
15921593 processing and again after the handshake is complete.
15931594
1595+ ## <a name =" alpn " >ALPN</a >
1596+
1597+ ` client-tls-alpn ` and ` server-tls-alpn ` demonstrate Application-Layer
1598+ Protocol Negotiation (ALPN, RFC 7301) through wolfSSL's OpenSSL-compatible
1599+ API. The client lists the application protocols it speaks in its
1600+ ClientHello, the server picks one, and both sides read the result after the
1601+ handshake.
1602+
1603+ The native wolfSSL API, ` wolfSSL_UseALPN() ` , takes a comma-separated list.
1604+ The OpenSSL-compatible calls used here take the list in wire format instead:
1605+ each name preceded by its length, so ` "h2,http/1.1" ` becomes
1606+ ` { 2,'h','2', 8,'h','t','t','p','/','1','.','1' } ` .
1607+
1608+ ` client-tls-alpn ` :
1609+
1610+ - ` wolfSSL_CTX_set_alpn_protos() ` sets the default list (` http/1.1,h2 ` ) for
1611+ every connection made from the context.
1612+ - ` wolfSSL_set_alpn_protos() ` overrides it for one connection when a list is
1613+ given on the command line.
1614+ - ` wolfSSL_get0_alpn_selected() ` reads the protocol the server chose. An
1615+ empty result means none was selected.
1616+
1617+ ` server-tls-alpn ` :
1618+
1619+ - ` wolfSSL_CTX_set_alpn_select_cb() ` registers a selection callback for every
1620+ connection, with the server's preference list (` h2,http/1.1 ` ) as its
1621+ argument, kept in wire format like the client's. The callback receives the
1622+ client's wire-format list and hands both lists to
1623+ ` wolfSSL_select_next_proto() ` , which walks the server's preferences in order
1624+ and picks the first one the client offered. The callback returns
1625+ ` SSL_TLSEXT_ERR_OK ` with that pick, or ` SSL_TLSEXT_ERR_ALERT_FATAL ` when
1626+ ` wolfSSL_select_next_proto() ` does not return ` WOLFSSL_NPN_NEGOTIATED `
1627+ (on no overlap it still fills in the client's first protocol, the NPN
1628+ fallback, so the return value must be checked). The fatal return fails the
1629+ handshake with a ` no_application_protocol ` alert, as RFC 7301 requires when
1630+ there is no common protocol. Returning ` SSL_TLSEXT_ERR_NOACK ` instead would
1631+ ignore the client's ALPN extension and continue with no protocol agreed.
1632+ - ` wolfSSL_set_alpn_select_cb() ` sets the callback and its argument for one
1633+ connection, overriding the context's. The server uses it when a preference
1634+ list is given on the command line.
1635+ - ` wolfSSL_ALPN_GetProtocol() ` reads the negotiated protocol and
1636+ ` wolfSSL_ALPN_GetPeerProtocol() ` the whole list the client offered, as a
1637+ comma-separated string released with ` wolfSSL_ALPN_FreePeerProtocol() ` .
1638+
1639+ ### Building
1640+
1641+ The selection callback and ` wolfSSL_get0_alpn_selected() ` are part of the
1642+ OpenSSL compatibility layer enabled by ` OPENSSL_ALL ` , and ALPN is off by
1643+ default, so build wolfSSL with both:
1644+
1645+ ``` sh
1646+ ./configure --enable-opensslall --enable-alpn && make && sudo make install
1647+ ```
1648+
1649+ Without them both programs print a message and exit. Note that
1650+ ` wolfSSL_CTX_set_alpn_protos() ` and ` wolfSSL_set_alpn_protos() ` return 0 on
1651+ success under ` --enable-opensslall ` (` WOLFSSL_ERROR_CODE_OPENSSL ` , matching
1652+ OpenSSL) and ` WOLFSSL_SUCCESS ` otherwise; the client handles both.
1653+
1654+ Then, in ` wolfssl-examples/tls ` :
1655+
1656+ ``` sh
1657+ make server-tls-alpn client-tls-alpn
1658+ ```
1659+
1660+ ### Running
1661+
1662+ Server in one terminal:
1663+
1664+ ``` sh
1665+ ./server-tls-alpn
1666+ Waiting for a connection...
1667+ ```
1668+
1669+ Client in another. The client lists ` http/1.1 ` first, the server prefers
1670+ ` h2 ` , and the server's preference wins:
1671+
1672+ ``` sh
1673+ ./client-tls-alpn 127.0.0.1
1674+ Offering ALPN protocols: http/1.1,h2
1675+ Server selected ALPN protocol: h2
1676+ Message for server: hello
1677+ Server: I hear ya fa shizzle!
1678+ ```
1679+
1680+ The server shows the callback's choice and the getters' results:
1681+
1682+ ```
1683+ ALPN callback: client offered http/1.1,h2; selected h2
1684+ Client connected successfully
1685+ Negotiated ALPN protocol: h2
1686+ Client offered: http/1.1,h2
1687+ Client: hello
1688+ ```
1689+
1690+ Offer only ` http/1.1 ` and that is what gets picked; offer something the
1691+ server does not speak and the handshake fails:
1692+
1693+ ``` sh
1694+ ./client-tls-alpn 127.0.0.1 http/1.1
1695+ Offering ALPN protocols: http/1.1
1696+ Server selected ALPN protocol: http/1.1
1697+
1698+ ./client-tls-alpn 127.0.0.1 spdy/3
1699+ Offering ALPN protocols: spdy/3
1700+ ERROR: failed to connect to wolfSSL: -313 (received alert fatal error)
1701+ ```
1702+
1703+ On the server side the rejection looks like this:
1704+
1705+ ```
1706+ ALPN callback: client offered spdy/3; no common protocol, rejecting the handshake
1707+ wolfSSL_accept error = -405 (Unrecognized protocol name Error)
1708+ ```
1709+
1710+ Start the server as ` ./server-tls-alpn http/1.1,h2 ` to flip its preference
1711+ for every connection through ` wolfSSL_set_alpn_select_cb() ` ; the default
1712+ client then gets ` http/1.1 ` . Send ` shutdown ` as the message to stop the
1713+ server. A rejected client does not stop it.
1714+
15941715
15951716## Support
15961717
0 commit comments