From 3c3eef95786c17b6c49c51da9ddbeeee76607f32 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 13 Sep 2024 13:18:47 +0800 Subject: [PATCH] Add socket option params to transport config in PJSUA2 (#4071) --- pjlib/include/pj/sock.h | 13 +++ pjlib/src/pj/sock_bsd.c | 11 ++ pjlib/src/pj/sock_common.c | 24 ++++ pjlib/src/pj/ssl_sock_common.c | 2 + .../main/java/org/pjsip/pjsua2/app/MyApp.java | 19 ++++ pjsip-apps/src/swig/pjsua2.i | 1 + pjsip/include/pjsip/sip_transport_tls.h | 2 + pjsip/include/pjsua2/siptypes.hpp | 106 +++++++++++++++++ pjsip/src/pjsip/sip_transport_tcp.c | 4 +- pjsip/src/pjsua2/json.cpp | 8 +- pjsip/src/pjsua2/siptypes.cpp | 107 ++++++++++++++++++ 11 files changed, 293 insertions(+), 4 deletions(-) diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index 2009216cb7..88d679c5e6 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -1517,6 +1517,19 @@ PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, ***************************************************************************** */ +/** + * Deep clone the socket options. + * + * @param pool The pool. + * @param dst Destination socket options. + * @param src Source socket options. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pj_sockopt_params_clone(pj_pool_t *pool, + pj_sockopt_params *dst, + const pj_sockopt_params *src); + /** * Print socket address string. This method will enclose the address string * with square bracket if it's IPv6 address. diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c index 035eb6c279..5f594efa74 100644 --- a/pjlib/src/pj/sock_bsd.c +++ b/pjlib/src/pj/sock_bsd.c @@ -25,6 +25,14 @@ #include #include +#if 0 + /* Enable some tracing */ + #include + #define TRACE_(arg) PJ_LOG(4,arg) +#else + #define TRACE_(arg) +#endif + #define THIS_FILE "sock_bsd.c" /* @@ -569,6 +577,7 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af, #endif *sock = socket(af, type, proto); + TRACE_((THIS_FILE, "Created new socket of type %d: %ld", type, *sock)); if (*sock == PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { @@ -832,6 +841,8 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, (const char*)optval, optlen); #else status = setsockopt(sock, level, optname, (const char*)optval, optlen); + TRACE_((THIS_FILE, "setsockopt %ld level:%d name:%d val:%d(%d)->%d", sock, + level, optname, *((const char *)optval), optlen, status)); #endif if (status != 0) diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c index 23f859707a..5c8d8a63dd 100644 --- a/pjlib/src/pj/sock_common.c +++ b/pjlib/src/pj/sock_common.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1205,6 +1206,29 @@ PJ_DEF(pj_status_t) pj_sock_setsockopt_sobuf( pj_sock_t sockfd, } +PJ_DEF(pj_status_t) pj_sockopt_params_clone(pj_pool_t *pool, + pj_sockopt_params *dst, + const pj_sockopt_params *src) +{ + unsigned int i; + + PJ_ASSERT_RETURN(pool && src && dst, PJ_EINVAL); + + pj_memcpy(dst, src, sizeof(pj_sockopt_params)); + for (i = 0; i < dst->cnt && i < PJ_MAX_SOCKOPT_PARAMS; ++i) { + if (dst->options[i].optlen == 0) { + dst->options[i].optval = NULL; + continue; + } + dst->options[i].optval = pj_pool_alloc(pool, dst->options[i].optlen); + pj_memcpy(dst->options[i].optval, src->options[i].optval, + dst->options[i].optlen); + } + + return PJ_SUCCESS; +} + + PJ_DEF(char *) pj_addr_str_print( const pj_str_t *host_str, int port, char *buf, int size, unsigned flag) { diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c index 65aba8ddab..4632ce079f 100644 --- a/pjlib/src/pj/ssl_sock_common.c +++ b/pjlib/src/pj/ssl_sock_common.c @@ -107,6 +107,8 @@ PJ_DEF(void) pj_ssl_sock_param_copy( pj_pool_t *pool, /* Path name must be null-terminated */ pj_strdup_with_null(pool, &dst->entropy_path, &src->entropy_path); } + + pj_sockopt_params_clone(pool, &dst->sockopt_params, &src->sockopt_params); } diff --git a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java index 1ebaeda8d7..adb994d7b9 100644 --- a/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java +++ b/pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2/app/MyApp.java @@ -416,6 +416,25 @@ public void init(MyAppObserver obs, String app_dir, } try { + /* Setting socket option parameters (uncomment this if needed). */ + /* + final int SOL_SOCKET = 1; + final int SOL_TCP = 6; + + final int SO_KEEPALIVE = 9; + final int TCP_KEEPIDLE = 4; + final int TCP_KEEPINTVL = 5; + final int TCP_KEEPCNT = 6; + + SockOptVector soVector = new SockOptVector(); + soVector.add(new SockOpt(SOL_SOCKET, SO_KEEPALIVE, 1)); + soVector.add(new SockOpt(SOL_TCP, TCP_KEEPIDLE, 1)); + soVector.add(new SockOpt(SOL_TCP, TCP_KEEPINTVL, 5)); + soVector.add(new SockOpt(SOL_TCP, TCP_KEEPCNT, 1)); + + sipTpConfig.getTlsConfig().getSockOptParams().setSockOpts(soVector); + */ + sipTpConfig.setPort(SIP_PORT+1); ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TLS, sipTpConfig); diff --git a/pjsip-apps/src/swig/pjsua2.i b/pjsip-apps/src/swig/pjsua2.i index 0aba16c0f0..5c7de25a79 100644 --- a/pjsip-apps/src/swig/pjsua2.i +++ b/pjsip-apps/src/swig/pjsua2.i @@ -166,6 +166,7 @@ using namespace pj; %include "pjsua2/siptypes.hpp" +%template(SockOptVector) std::vector; %template(SipHeaderVector) std::vector; %template(AuthCredInfoVector) std::vector; %template(SrtpCryptoVector) std::vector; diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h index ce9aab95ca..8c0d014f35 100644 --- a/pjsip/include/pjsip/sip_transport_tls.h +++ b/pjsip/include/pjsip/sip_transport_tls.h @@ -500,6 +500,8 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool, for (i=0; icurves_num; ++i) dst->curves[i] = src->curves[i]; } + + pj_sockopt_params_clone(pool, &dst->sockopt_params, &src->sockopt_params); } diff --git a/pjsip/include/pjsua2/siptypes.hpp b/pjsip/include/pjsua2/siptypes.hpp index 7018f404e1..9705bcc711 100644 --- a/pjsip/include/pjsua2/siptypes.hpp +++ b/pjsip/include/pjsua2/siptypes.hpp @@ -126,6 +126,86 @@ struct AuthCredInfo : public PersistentObject ////////////////////////////////////////////////////////////////////////////// +/** + * Socket option type. + */ +struct SockOpt { + /** + * The level at which the option is defined. + */ + int level; + + /** + * Option name. + */ + int optName; + +public: + /** Default constructor. */ + SockOpt(); + + /** Construct a socket option with the specified parameters. */ + SockOpt(int level, int optName, int optVal); + + /** + * Set option value of type integer. + * + * @param opt_val Option value. + */ + void setOptValInt(int opt_val); + +private: + friend struct SockOptParams; + + /** Pointer to the buffer in which the option is specified. */ + void *optVal; + + /** Buffer size of the buffer pointed by optVal. */ + int optLen; + + /** Option value if the type is integer. */ + int optValInt; +}; + +/** Array of socket options */ +typedef std::vector SockOptVector; + +/** + * Socket option parameters, to be specified in TransportConfig. + */ +struct SockOptParams : public PersistentObject +{ + /** + * Array of socket options. + */ + SockOptVector sockOpts; + +public: + /** Default constructor initialises with default values */ + SockOptParams(); + + /** Convert to pjsip */ + pj_sockopt_params toPj() const; + + /** Convert from pjsip */ + void fromPj(const pj_sockopt_params &prm); + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) PJSUA2_THROW(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const PJSUA2_THROW(Error); +}; + + /** * TLS transport settings, to be specified in TransportConfig. */ @@ -300,6 +380,22 @@ struct TlsConfig : public PersistentObject */ bool qosIgnoreError; + /** + * Specify options to be set on the transport. + * + * By default, this is unset, which means that the underlying sockopt + * params as returned by #pj_ssl_sock_param_default() will be used. + */ + SockOptParams sockOptParams; + + /** + * Specify if the transport should ignore any errors when setting the + * sockopt parameters. + * + * Default: true + */ + bool sockOptIgnoreError; + /** * Specify if renegotiation is enabled for TLSv1.2 or earlier. * @@ -424,6 +520,16 @@ struct TransportConfig : public PersistentObject */ pj_qos_params qosParams; + /** + * Set the low level socket options to the transport. + * + * For TLS transport, this field will be ignored, the socket options + * can be set via tlsConfig. + * + * Default is no socket option set. + */ + SockOptParams sockOptParams; + public: /** Default constructor initialises with default values */ TransportConfig(); diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 303a57ee9e..973ecfb0ea 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -401,8 +401,8 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( listener->initial_timeout = cfg->initial_timeout; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); - pj_memcpy(&listener->sockopt_params, &cfg->sockopt_params, - sizeof(cfg->sockopt_params)); + pj_sockopt_params_clone(pool, &listener->sockopt_params, + &cfg->sockopt_params); pj_ansi_strxcpy(listener->factory.obj_name, "tcptp", sizeof(listener->factory.obj_name)); diff --git a/pjsip/src/pjsua2/json.cpp b/pjsip/src/pjsua2/json.cpp index 253b98681d..a34c1550d9 100644 --- a/pjsip/src/pjsua2/json.cpp +++ b/pjsip/src/pjsua2/json.cpp @@ -473,7 +473,9 @@ static void jsonNode_writeString(ContainerNode *node, pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_str_t new_val; - pj_strdup2(jdat->doc->getPool(), &new_val, value.c_str()); + pj_str_t str_val; + pj_strset(&str_val, (char*)value.data(), value.size()); + pj_strdup(jdat->doc->getPool(), &new_val, &str_val); pj_json_elem_string(el, &nm, &new_val); pj_json_elem_add(jdat->jnode, el); @@ -491,8 +493,10 @@ static void jsonNode_writeStringVector(ContainerNode *node, pj_json_elem_array(el, &nm); for (unsigned i=0; idoc->getPool(), &new_val, value[i].c_str()); + pj_strset(&str_val, (char*)value[i].data(), value[i].size()); + pj_strdup(jdat->doc->getPool(), &new_val, &str_val); pj_json_elem *child = jdat->doc->allocElement(); pj_json_elem_string(child, NULL, &new_val); pj_json_elem_add(el, child); diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp index dd5716c5d4..6416964a46 100644 --- a/pjsip/src/pjsua2/siptypes.cpp +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -209,6 +209,8 @@ pjsip_tls_setting TlsConfig::toPj() const ts.qos_type = this->qosType; ts.qos_params = this->qosParams; ts.qos_ignore_error = this->qosIgnoreError; + ts.sockopt_params = this->sockOptParams.toPj(); + ts.sockopt_ignore_error = this->sockOptIgnoreError; ts.enable_renegotiation = this->enableRenegotiation; return ts; @@ -237,6 +239,8 @@ void TlsConfig::fromPj(const pjsip_tls_setting &prm) this->qosType = prm.qos_type; this->qosParams = prm.qos_params; this->qosIgnoreError = PJ2BOOL(prm.qos_ignore_error); + this->sockOptParams.fromPj(prm.sockopt_params); + this->sockOptIgnoreError = PJ2BOOL(prm.sockopt_ignore_error); this->enableRenegotiation = PJ2BOOL(prm.enable_renegotiation); } @@ -260,6 +264,8 @@ void TlsConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); readQosParams ( this_node, qosParams); NODE_READ_BOOL ( this_node, qosIgnoreError); + NODE_READ_OBJ ( this_node, sockOptParams); + NODE_READ_BOOL ( this_node, sockOptIgnoreError); NODE_READ_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType); NODE_READ_STRING ( this_node, certLookupKeyword); } @@ -284,12 +290,109 @@ void TlsConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); writeQosParams ( this_node, qosParams); NODE_WRITE_BOOL ( this_node, qosIgnoreError); + NODE_WRITE_OBJ ( this_node, sockOptParams); + NODE_WRITE_BOOL ( this_node, sockOptIgnoreError); NODE_WRITE_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType); NODE_WRITE_STRING ( this_node, certLookupKeyword); } /////////////////////////////////////////////////////////////////////////////// +SockOpt::SockOpt() +{ + pj_bzero(this, sizeof(*this)); +} + +SockOpt::SockOpt(int level, int optName, int optVal) +{ + pj_bzero(this, sizeof(*this)); + this->level = level; + this->optName = optName; + setOptValInt(optVal); +} + +void SockOpt::setOptValInt(int opt_val) +{ + optVal = &optValInt; + optLen = sizeof(int); + optValInt = opt_val; +} + +SockOptParams::SockOptParams() +{ +} + +pj_sockopt_params SockOptParams::toPj() const +{ + pj_sockopt_params sop; + unsigned i; + + pj_bzero(&sop, sizeof(sop)); + sop.cnt = this->sockOpts.size(); + if (sop.cnt > PJ_MAX_SOCKOPT_PARAMS) + sop.cnt = PJ_MAX_SOCKOPT_PARAMS; + for (i = 0; i < sop.cnt; ++i) { + sop.options[i].level = this->sockOpts[i].level; + sop.options[i].optname = this->sockOpts[i].optName; + sop.options[i].optval = this->sockOpts[i].optVal; + sop.options[i].optlen = this->sockOpts[i].optLen; + } + + return sop; +} + +void SockOptParams::fromPj(const pj_sockopt_params &prm) +{ + unsigned i; + + this->sockOpts.clear(); + for (i = 0; i < prm.cnt; ++i) { + SockOpt so; + so.level = prm.options[i].level; + so.optName = prm.options[i].optname; + if (prm.options[i].optlen == sizeof(int)) { + so.setOptValInt(*((int *)prm.options[i].optval)); + } + this->sockOpts.push_back(so); + } +} + +void SockOptParams::readObject(const ContainerNode &node) PJSUA2_THROW(Error) +{ + ContainerNode array_node = node.readArray("sockOptParams"); + sockOpts.resize(0); + while (array_node.hasUnread()) { + ContainerNode so_node = array_node.readContainer("sockOpt"); + SockOpt so; + so.level = so_node.readInt("level"); + so.optName = so_node.readInt("optName"); + so.optLen = so_node.readInt("optLen"); + if (so.optLen == sizeof(int)) { + int optVal = so_node.readInt("optVal"); + so.setOptValInt(optVal); + } + sockOpts.push_back(so); + } +} + +void SockOptParams::writeObject(ContainerNode &node) const PJSUA2_THROW(Error) +{ + ContainerNode array_node = node.writeNewArray("sockOptParams"); + for (unsigned i=0; i 0? sockOpts[i].optLen : 0); + so_node.writeInt("level", sockOpts[i].level); + so_node.writeInt("optName", sockOpts[i].optName); + so_node.writeInt("optLen", sockOpts[i].optLen); + if (sockOpts[i].optLen == sizeof(int)) { + so_node.writeInt("optVal", sockOpts[i].optValInt); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + TransportConfig::TransportConfig() : qosType(PJ_QOS_TYPE_BEST_EFFORT) { pjsua_transport_config tc; @@ -307,6 +410,7 @@ void TransportConfig::fromPj(const pjsua_transport_config &prm) this->tlsConfig.fromPj(prm.tls_setting); this->qosType = prm.qos_type; this->qosParams = prm.qos_params; + this->sockOptParams.fromPj(prm.sockopt_params); } pjsua_transport_config TransportConfig::toPj() const @@ -322,6 +426,7 @@ pjsua_transport_config TransportConfig::toPj() const tc.tls_setting = this->tlsConfig.toPj(); tc.qos_type = this->qosType; tc.qos_params = this->qosParams; + tc.sockopt_params = this->sockOptParams.toPj(); return tc; } @@ -337,6 +442,7 @@ void TransportConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error) NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); readQosParams ( this_node, qosParams); NODE_READ_OBJ ( this_node, tlsConfig); + NODE_READ_OBJ ( this_node, sockOptParams); } void TransportConfig::writeObject(ContainerNode &node) const @@ -351,6 +457,7 @@ void TransportConfig::writeObject(ContainerNode &node) const NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); writeQosParams ( this_node, qosParams); NODE_WRITE_OBJ ( this_node, tlsConfig); + NODE_WRITE_OBJ ( this_node, sockOptParams); } ///////////////////////////////////////////////////////////////////////////////