@@ -39,6 +39,7 @@ extern struct k_work_q mosh_common_work_q;
3939#define SOCK_RECEIVE_BUFFER_SIZE 1536
4040#define SOCK_RECEIVE_STACK_SIZE 1280
4141#define SOCK_RECEIVE_PRIORITY 5
42+ #define SOCK_OPT_VALUE_LIST_SIZE 20
4243/* Timeout (in ms) for polling socket events such as receive data,
4344 * permission to send more, disconnected socket etc.
4445 * This limits how quickly data can be received after socket creation.
@@ -1305,6 +1306,249 @@ int sock_rai(int socket_id, bool rai_last, bool rai_no_data,
13051306 return 0 ;
13061307}
13071308
1309+ int sock_setopt (int socket_id , int sock_level , int sock_opt_id , char * sock_opt_value )
1310+ {
1311+ struct sock_info * socket_info = get_socket_info_by_id (socket_id );
1312+ int err ;
1313+ char * end ;
1314+
1315+ if (socket_info == NULL ) {
1316+ return - EINVAL ;
1317+ }
1318+
1319+ if (sock_opt_id == 0 ) {
1320+ mosh_error ("Socket option is mandatory." );
1321+ return - EINVAL ;
1322+ }
1323+
1324+ if (sock_opt_value == NULL &&
1325+ !(sock_level == SOL_TLS && sock_opt_id == TLS_HOSTNAME ) &&
1326+ !(sock_level == SOL_TLS && sock_opt_id == TLS_SESSION_CACHE_PURGE ) &&
1327+ !(sock_level == SOL_TLS && sock_opt_id == TLS_DTLS_CONN_SAVE ) &&
1328+ !(sock_level == SOL_TLS && sock_opt_id == TLS_DTLS_CONN_LOAD )) {
1329+ mosh_error ("Socket option value is mandatory." );
1330+ return - EINVAL ;
1331+ }
1332+
1333+ switch (sock_level ) {
1334+ case SOL_TLS :
1335+ switch (sock_opt_id ) {
1336+ case TLS_SEC_TAG_LIST :
1337+ case TLS_CIPHERSUITE_LIST :
1338+ /* Value is a comma separated list of integers or hexadecimals, for example
1339+ * "42,43,44" or "0xC024,0xC00A,0xC023".
1340+ */
1341+ int value_list [SOCK_OPT_VALUE_LIST_SIZE ];
1342+ unsigned int value_count = 0 ;
1343+
1344+ for (int i = 0 ; i < strlen (sock_opt_value ); i ++ ) {
1345+ if (sock_opt_value [i ] == ',' ) {
1346+ sock_opt_value [i ] = ' ' ;
1347+ }
1348+ }
1349+
1350+ while (value_count < ARRAY_SIZE (value_list )) {
1351+ int value = strtoul (sock_opt_value , & end , 0 );
1352+
1353+ if (end == sock_opt_value ) {
1354+ break ;
1355+ }
1356+ value_list [value_count ++ ] = value ;
1357+ sock_opt_value = end ;
1358+ }
1359+
1360+ err = setsockopt (socket_info -> fd , sock_level , sock_opt_id , value_list ,
1361+ value_count * sizeof (int ));
1362+ goto exit ;
1363+
1364+ case TLS_HOSTNAME :
1365+ /* Value can be empty to clear hostname. */
1366+ size_t hostname_len ;
1367+
1368+ if (sock_opt_value == NULL ) {
1369+ hostname_len = 0 ;
1370+ } else {
1371+ hostname_len = strlen (sock_opt_value );
1372+ }
1373+ err = setsockopt (socket_info -> fd , sock_level , sock_opt_id , sock_opt_value ,
1374+ hostname_len );
1375+ goto exit ;
1376+
1377+ case TLS_SESSION_CACHE_PURGE :
1378+ case TLS_DTLS_CONN_SAVE :
1379+ case TLS_DTLS_CONN_LOAD :
1380+ /* No value used for these options. */
1381+ err = setsockopt (socket_info -> fd , sock_level , sock_opt_id , NULL , 0 );
1382+ goto exit ;
1383+
1384+ default :
1385+ break ;
1386+ }
1387+ break ; /* SOL_TLS */
1388+
1389+ case SOL_SOCKET :
1390+ switch (sock_opt_id ) {
1391+ case SO_RCVTIMEO :
1392+ case SO_SNDTIMEO :
1393+ /* Value is in milliseconds and needs to be converted into
1394+ * a timeval structure.
1395+ */
1396+ int timeout_us ;
1397+ struct timeval tv ;
1398+
1399+ timeout_us = atoi (sock_opt_value ) * USEC_PER_MSEC ;
1400+ tv .tv_sec = timeout_us / USEC_PER_SEC ;
1401+ tv .tv_usec = timeout_us % USEC_PER_SEC ;
1402+ err = setsockopt (socket_info -> fd , sock_level , sock_opt_id , & tv ,
1403+ sizeof (tv ));
1404+ goto exit ;
1405+
1406+ default :
1407+ break ;
1408+ }
1409+ break ; /* SOL_SOCKET */
1410+
1411+ default :
1412+ break ;
1413+ }
1414+
1415+ /* All other values are integers. */
1416+ int sock_opt_value_int ;
1417+
1418+ sock_opt_value_int = atoi (sock_opt_value );
1419+ err = setsockopt (socket_info -> fd , sock_level , sock_opt_id , & sock_opt_value_int ,
1420+ sizeof (sock_opt_value_int ));
1421+
1422+ exit :
1423+ if (err ) {
1424+ mosh_error ("setsockopt() for level=%d, option=%d, value=%s failed with error %d" ,
1425+ sock_level , sock_opt_id , sock_opt_value , errno );
1426+ }
1427+
1428+ return err ;
1429+ }
1430+
1431+ int sock_getopt (int socket_id , int sock_level , int sock_opt_id )
1432+ {
1433+ struct sock_info * socket_info = get_socket_info_by_id (socket_id );
1434+ int err ;
1435+ int sock_opt_value = 0 ;
1436+ int sock_opt_value_len = sizeof (sock_opt_value );
1437+
1438+ if (socket_info == NULL ) {
1439+ return - EINVAL ;
1440+ }
1441+
1442+ switch (sock_level ) {
1443+ case SOL_TLS :
1444+ switch (sock_opt_id ) {
1445+ case TLS_SEC_TAG_LIST :
1446+ case TLS_CIPHERSUITE_LIST :
1447+ /* Value is a list of sec_tags or ciphersuites. */
1448+ int value_list [SOCK_OPT_VALUE_LIST_SIZE ];
1449+ unsigned int value_list_size = sizeof (value_list );
1450+
1451+ err = getsockopt (socket_info -> fd , sock_level , sock_opt_id , value_list ,
1452+ & value_list_size );
1453+ if (!err ) {
1454+ char value_str [128 ] = { 0 };
1455+ size_t value_str_len = 0 ;
1456+
1457+ for (unsigned int i = 0 ; i < value_list_size / sizeof (int ); i ++ ) {
1458+ if (sock_opt_id == TLS_SEC_TAG_LIST ) {
1459+ value_str_len +=
1460+ snprintf (value_str + value_str_len ,
1461+ sizeof (value_str ) - value_str_len ,
1462+ "%d," , value_list [i ]);
1463+ } else {
1464+ value_str_len +=
1465+ snprintf (value_str + value_str_len ,
1466+ sizeof (value_str ) - value_str_len ,
1467+ "0x%X," , value_list [i ]);
1468+ }
1469+ if (value_str_len >= sizeof (value_str ) - 1 ) {
1470+ break ;
1471+ }
1472+ }
1473+
1474+ if (value_str_len > 0 && value_str_len <= sizeof (value_str )) {
1475+ if (value_str [value_str_len - 1 ] == ',' ) {
1476+ value_str [value_str_len - 1 ] = '\0' ;
1477+ }
1478+ }
1479+
1480+ mosh_print (
1481+ "getsockopt() socket_id=%d, level=%d, option=%d, value=%s" ,
1482+ socket_id , sock_level , sock_opt_id , value_str );
1483+ }
1484+ goto exit ;
1485+
1486+ case TLS_HOSTNAME :
1487+ /* Value is a string. */
1488+ char hostname [64 ] = { 0 };
1489+ size_t hostname_len = sizeof (hostname ) - 1 ;
1490+
1491+ err = getsockopt (socket_info -> fd , sock_level , sock_opt_id ,
1492+ hostname , & hostname_len );
1493+ if (!err ) {
1494+ mosh_print (
1495+ "getsockopt() socket_id=%d, level=%d, option=%d, value=%s" ,
1496+ socket_id , sock_level , sock_opt_id , hostname );
1497+ }
1498+ goto exit ;
1499+
1500+ default :
1501+ break ;
1502+ }
1503+ break ; /* SOL_TLS */
1504+
1505+ case SOL_SOCKET :
1506+ switch (sock_opt_id ) {
1507+ case SO_RCVTIMEO :
1508+ case SO_SNDTIMEO :
1509+ /* Value is a timeval structure. */
1510+ struct timeval tv ;
1511+ size_t tv_len = sizeof (tv );
1512+
1513+ err = getsockopt (socket_info -> fd , sock_level , sock_opt_id , & tv , & tv_len );
1514+ if (!err ) {
1515+ int timeout_ms = tv .tv_sec * MSEC_PER_SEC +
1516+ tv .tv_usec / USEC_PER_MSEC ;
1517+ mosh_print (
1518+ "getsockopt() socket_id=%d, level=%d, option=%d, "
1519+ "value=%d ms" ,
1520+ socket_id , sock_level , sock_opt_id , timeout_ms );
1521+ }
1522+ goto exit ;
1523+
1524+ default :
1525+ break ;
1526+ }
1527+ break ; /* SOL_SOCKET */
1528+
1529+ default :
1530+ break ;
1531+ }
1532+
1533+ /* All other values are integers. */
1534+ err = getsockopt (socket_info -> fd , sock_level , sock_opt_id , & sock_opt_value ,
1535+ & sock_opt_value_len );
1536+ if (!err ) {
1537+ mosh_print (
1538+ "getsockopt() socket_id=%d, level=%d, option=%d, value=%d" ,
1539+ socket_id , sock_level , sock_opt_id , sock_opt_value );
1540+ }
1541+
1542+ exit :
1543+ if (err ) {
1544+ mosh_error (
1545+ "getsockopt() socket_id=%d, level=%d, option=%d failed with error %d" ,
1546+ socket_id , sock_level , sock_opt_id , errno );
1547+ }
1548+
1549+ return err ;
1550+ }
1551+
13081552int sock_list (void )
13091553{
13101554 bool opened_sockets = false;
0 commit comments