@@ -72,6 +72,9 @@ ffi.cdef[[
7272 int ngx_stream_lua_ffi_ssl_client_random(ngx_stream_lua_request_t *r,
7373 unsigned char *out, size_t *outlen, char **err);
7474
75+ int ngx_stream_lua_ffi_req_shared_ssl_ciphers(void *r, uint16_t *ciphers,
76+ uint16_t *nciphers, int filter_grease, char **err);
77+
7578]]
7679_EOC_
7780 }
@@ -1374,3 +1377,195 @@ SUCCESS
13741377--- no_error_log
13751378[error]
13761379[alert]
1380+
1381+
1382+
1383+ === TEST 14: Get supported ciphers
1384+ --- stream_config
1385+ server {
1386+ listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
1387+
1388+ ssl_certificate_by_lua_block {
1389+ collectgarbage()
1390+ require "defines"
1391+ local ffi = require "ffi"
1392+ local cjson = require "cjson.safe"
1393+
1394+ local MAX_CIPHERS = 64
1395+ local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
1396+ local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
1397+ local err = ffi.new("char*[1]")
1398+
1399+ local r = require "resty.core.base" .get_request()
1400+ if not r then
1401+ ngx.log(ngx.ERR, "no request found")
1402+ return
1403+ end
1404+ local ret = ffi.C.ngx_stream_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
1405+
1406+ if ret ~= 0 then
1407+ ngx.log(ngx.ERR, "error getting ciphers: ", ffi.string(err[0]))
1408+ else
1409+ local res = {}
1410+ for i = 0, nciphers[0] - 1 do
1411+ local cipher_id = string.format("%04x", ciphers[i])
1412+ table.insert(res, cipher_id)
1413+ end
1414+ ngx.log(ngx.INFO, "supported ciphers: ", cjson.encode(res))
1415+ end
1416+ }
1417+
1418+ ssl_certificate ../../cert/test.crt;
1419+ ssl_certificate_key ../../cert/test.key;
1420+ ssl_protocols TLSv1.2;
1421+ ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
1422+
1423+ return 'cipher test works!\n';
1424+ }
1425+ --- stream_server_config
1426+ proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1427+ proxy_ssl on;
1428+ proxy_ssl_session_reuse off;
1429+ proxy_ssl_protocols TLSv1.2;
1430+ proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
1431+ --- stream_response
1432+ cipher test works!
1433+ --- error_log
1434+ supported ciphers: ["c02f","c02b"]
1435+ --- no_error_log
1436+ [error]
1437+ [alert]
1438+
1439+
1440+
1441+ === TEST 15: Get supported ciphers with GREASE filtering
1442+ --- stream_config
1443+ server {
1444+ listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
1445+
1446+ ssl_certificate_by_lua_block {
1447+ collectgarbage()
1448+ require "defines"
1449+ local ffi = require "ffi"
1450+ local cjson = require "cjson.safe"
1451+ local MAX_CIPHERS = 64
1452+ local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
1453+ local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
1454+ local err = ffi.new("char*[1]")
1455+
1456+ local r = require "resty.core.base" .get_request()
1457+ if not r then
1458+ ngx.log(ngx.ERR, "no request found")
1459+ return
1460+ end
1461+
1462+ -- Test without GREASE filtering
1463+ local ret = ffi.C.ngx_stream_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
1464+ local res_no_filter = {}
1465+ if ret == 0 then
1466+ for i = 0, nciphers[0] - 1 do
1467+ local cipher_id = string.format("%04x", ciphers[i])
1468+ table.insert(res_no_filter, cipher_id)
1469+ end
1470+ end
1471+
1472+ -- Reset buffer for next test
1473+ nciphers[0] = MAX_CIPHERS
1474+
1475+ -- Test with GREASE filtering
1476+ local ret = ffi.C.ngx_stream_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 1, err)
1477+ local res_with_filter = {}
1478+ if ret == 0 then
1479+ for i = 0, nciphers[0] - 1 do
1480+ local cipher_id = string.format("%04x", ciphers[i])
1481+ table.insert(res_with_filter, cipher_id)
1482+ end
1483+ end
1484+
1485+ ngx.log(ngx.INFO, "without_filter: ", cjson.encode(res_no_filter))
1486+ ngx.log(ngx.INFO, "with_filter: ", cjson.encode(res_with_filter))
1487+ }
1488+
1489+ ssl_certificate ../../cert/test.crt;
1490+ ssl_certificate_key ../../cert/test.key;
1491+ ssl_protocols TLSv1.2;
1492+ ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
1493+
1494+ return 'grease filter test works!\n';
1495+ }
1496+ --- stream_server_config
1497+ proxy_pass unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1498+ proxy_ssl on;
1499+ proxy_ssl_certificate ../../cert/mtls_client.crt;
1500+ proxy_ssl_certificate_key ../../cert/mtls_client.key;
1501+ proxy_ssl_session_reuse off;
1502+
1503+ --- stream_response
1504+ grease filter test works!
1505+ --- error_log
1506+ without_filter:
1507+ with_filter:
1508+ --- no_error_log
1509+ [error]
1510+ [alert]
1511+
1512+
1513+
1514+ === TEST 16: SSL cipher API error handling (no SSL)
1515+ --- stream_config
1516+ server {
1517+ listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
1518+
1519+ content_by_lua_block {
1520+ require "defines"
1521+ local ffi = require "ffi"
1522+
1523+ local MAX_CIPHERS = 64
1524+ local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
1525+ local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
1526+ local err = ffi.new("char*[1]")
1527+
1528+ local r = require "resty.core.base" .get_request()
1529+ if not r then
1530+ ngx.log(ngx.ERR, "no request found")
1531+ return
1532+ end
1533+ local ret = ffi.C.ngx_stream_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
1534+
1535+ if ret ~= 0 then
1536+ ngx.say("error: ", ffi.string(err[0]))
1537+ else
1538+ ngx.say("unexpected success")
1539+ end
1540+ }
1541+ }
1542+ --- stream_server_config
1543+ content_by_lua_block {
1544+ local sock = ngx.socket.tcp()
1545+ sock:settimeout(2000)
1546+
1547+ local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
1548+ if not ok then
1549+ ngx.log(ngx.ERR, "failed to connect: ", err)
1550+ return
1551+ end
1552+
1553+ local line, err = sock:receive()
1554+ if not line then
1555+ ngx.log(ngx.ERR, "failed to receive: ", err)
1556+ return
1557+ end
1558+
1559+ ngx.say("received: ", line)
1560+
1561+ local ok, err = sock:close()
1562+ ngx.say("close: ", ok, " ", err)
1563+ }
1564+
1565+ --- stream_response
1566+ received: error: bad request
1567+ close: 1 nil
1568+
1569+ --- no_error_log
1570+ [error]
1571+ [alert]
0 commit comments