michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "CSFLog.h" michael@0: michael@0: #include "CC_Common.h" michael@0: #include "csf_common.h" michael@0: #ifdef WIN32 michael@0: #include michael@0: #else michael@0: #ifdef LINUX michael@0: // for platGetIPAddr michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: #endif michael@0: michael@0: #include "cpr_string.h" michael@0: michael@0: static const char* logTag = "sipcc"; michael@0: michael@0: extern "C" michael@0: { michael@0: #include "plat_api.h" michael@0: #include michael@0: michael@0: michael@0: void NotifyStateChange (cc_callid_t callid, int32_t state) { michael@0: //Don't need anything here. michael@0: //Call state change are notified to us via SIPCC "high level" API michael@0: } michael@0: michael@0: #ifndef OSX michael@0: /** michael@0: * platGetFeatureAllowed michael@0: * michael@0: * Get whether the feature is allowed michael@0: * michael@0: * @param featureId - sis feature id michael@0: * michael@0: * @return 1 - allowed, 0 - not allowed michael@0: * michael@0: */ michael@0: int platGetFeatureAllowed(cc_sis_feature_id_e featureId) { michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Set the Status message for failure reasons michael@0: * @param char *msg michael@0: * @return void michael@0: */ michael@0: void platSetStatusMessage(char *msg) { michael@0: } michael@0: michael@0: /** michael@0: * Sets the time based on Date header in 200 OK from REGISTER request michael@0: * @param void michael@0: * @return void michael@0: */ michael@0: void platSetCucmRegTime (void) { michael@0: } michael@0: michael@0: /** michael@0: * Enable / disable speaker michael@0: * michael@0: * @param[in] state - true -> enable speaker, false -> disable speaker michael@0: * michael@0: * @return void michael@0: */ michael@0: extern "C" void platSetSpeakerMode(cc_boolean state) { michael@0: } michael@0: michael@0: /** michael@0: * Get the status (on/off) of the audio device michael@0: * michael@0: * @param[in] device_type - headset or speaker (see vcm_audio_device_t) michael@0: * michael@0: * @return 1 -> On, 0 -> off, ERROR -> unknown (error) michael@0: */ michael@0: int platGetAudioDeviceStatus(plat_audio_device_t device_type) { michael@0: //Tell SIPCC what the current audio path is by return 1 for one of either: headset or speaker. michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Check if the speaker or headset is enabled. michael@0: * michael@0: * @return boolean if the speaker or headset is enabled, returns true. michael@0: */ michael@0: boolean platGetSpeakerHeadsetMode() { michael@0: return TRUE; michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Provides the local MAC address michael@0: * michael@0: * @param *maddr the pointer to the string holding MAC address michael@0: * in the MAC address format after converting from string format. michael@0: * @return void michael@0: */ michael@0: void platGetMacAddr(char *maddr) { michael@0: //Strictly speaking the code using this is not treating this as a string. michael@0: //It's taking the first 6 bytes out of the buffer, and printing these michael@0: //directly, so it's not enough to just make the first byte '\0' need michael@0: //to set all of the bytes in range 0-5 equal to '\0'. michael@0: //Have to assume here that the buffer is big enough. michael@0: for (int i=0; i<6; i++) michael@0: { michael@0: *(maddr+i) = '\0'; michael@0: } michael@0: } michael@0: michael@0: #ifndef OSX michael@0: /** michael@0: * Called by the thread to initialize any thread specific data michael@0: * once the thread is created. michael@0: * michael@0: * @param[in] tname thread name michael@0: * michael@0: * @return 0 - SUCCESS michael@0: * -1 - FAILURE michael@0: */ michael@0: int platThreadInit(char * tname) { michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * The initial initialization function for any platform related michael@0: * modules michael@0: * michael@0: * michael@0: * @return 0 - SUCCESS michael@0: * -1 - FAILURE michael@0: */ michael@0: int platInit() { michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * The initial initialization function for the debugging/logging michael@0: * modules michael@0: * michael@0: */ michael@0: void debugInit() { michael@0: return ; michael@0: } michael@0: michael@0: /** michael@0: * Add cc control classifier michael@0: * michael@0: * Called by SIP stack to specify addresses and ports that will be used for call control michael@0: * michael@0: * @param[in] myIPAddr - phone local interface IP Address michael@0: * @param[in] myPort - phone local interface Port michael@0: * @param[in] cucm1IPAddr - CUCM 1 IP Address michael@0: * @param[in] cucm1Port - CUCM 1 Port michael@0: * @param[in] cucm2IPAddr - CUCM 2 IP Address michael@0: * @param[in] cucm2Port - CUCM 2 Port michael@0: * @param[in] cucm3IPAddr - CUCM 3 IP Address michael@0: * @param[in] cucm3Port - CUCM 3 Port michael@0: * @param[in] protocol - CC_IPPROTO_UDP or CC_IP_PROTO_TCP michael@0: * michael@0: * @note : Needed only if using WiFi. If not using Wifi please provide a stub michael@0: */ michael@0: void platAddCallControlClassifiers(unsigned long myIPAddr, unsigned short myPort, michael@0: unsigned long cucm1IPAddr, unsigned short cucm1Port, michael@0: unsigned long cucm2IPAddr, unsigned short cucm2Port, michael@0: unsigned long cucm3IPAddr, unsigned short cucm3Port, michael@0: unsigned char protocol) { michael@0: //Needed only if using WiFi. If not using Wifi please provide a stub michael@0: } michael@0: michael@0: /** michael@0: * Remove cc control classifier. michael@0: * michael@0: * Undo platAddCallControlClassifiers michael@0: */ michael@0: void platRemoveCallControlClassifiers() { michael@0: //Needed only if using WiFi. If not using Wifi please provide a stub michael@0: } michael@0: michael@0: /** michael@0: * Set ip address mode michael@0: * e.g. michael@0: * michael@0: */ michael@0: cpr_ip_mode_e platGetIpAddressMode() { michael@0: return CPR_IP_MODE_IPV4; michael@0: } michael@0: michael@0: /** michael@0: * Tell whether wifi is supported and active michael@0: * michael@0: * @return boolean wether WLAN is active or not michael@0: */ michael@0: cc_boolean platWlanISActive() { michael@0: return FALSE; michael@0: } michael@0: michael@0: /** michael@0: * Check if the network interface changed. michael@0: * michael@0: * @return boolean returns TRUE if the network interface has changed michael@0: * michael@0: * @note Most common case is for softphone clients where if a PC is michael@0: * undocked the network interface changes from wired to wireless. michael@0: */ michael@0: boolean platIsNetworkInterfaceChanged() { michael@0: //We're OK with this michael@0: return FALSE; michael@0: } michael@0: michael@0: /** michael@0: * Get active phone load name michael@0: * michael@0: * Returns the phone images in the active and inactive partitions michael@0: * The phone reports these phone loads to CUCM for display on the Admin page michael@0: * michael@0: * @param[in] image_a : Populate the image name from partition a michael@0: * @param[in] image_b : Populate the image name from partition b michael@0: * @param[in] len : Length of the pointers for image_a and image_b michael@0: * @return 1 - image_a is active. michael@0: * Anything other than 1 - image_b is active michael@0: */ michael@0: int platGetActiveInactivePhoneLoadName(char * image_a, char * image_b, int len) { michael@0: if (image_a != nullptr) michael@0: { michael@0: sstrncpy(image_a, "image_a", len); michael@0: } michael@0: michael@0: if (image_b != nullptr) michael@0: { michael@0: sstrncpy(image_b, "image_b", len); michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: /** michael@0: * Get or Set user defined phrases michael@0: * @param index the phrase index, see michael@0: * @param phrase the return phrase holder michael@0: * @param len the input length to cap the maximum value michael@0: * @return SUCCESS or FAILURE michael@0: */ michael@0: int platGetPhraseText(int index, char* phrase, unsigned int len) { michael@0: //Need to copy something into "phrase" as this is used as a prefix michael@0: //in a starts with comparison (use strncmp) that will match against michael@0: //any string if the prefix is empty. Also in some places an michael@0: //uninitialized buffer is passed in as "phrase", so if we don't michael@0: //do something then SIPCC will go on the use the uninitialized michael@0: //buffer. michael@0: michael@0: if (phrase == nullptr) michael@0: { michael@0: return CC_FAILURE; michael@0: } michael@0: michael@0: sstrncpy(phrase, "?????", len); michael@0: michael@0: return (int) CC_SUCCESS; michael@0: } michael@0: michael@0: /** michael@0: * Get the unregistration reason code. michael@0: * @return reason code for unregistration, see the definition. michael@0: */ michael@0: int platGetUnregReason() { michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Set the unregistration reason michael@0: * @param reason see the unregister reason definitions. michael@0: * @return void michael@0: */ michael@0: void platSetUnregReason(int reason) { michael@0: //We may need to persist this for CUCM. WHen we restart next time call to platGetUnregReason above tells CUCM what why we unregistered last time. michael@0: typedef struct _unRegRreasonEnumPair { michael@0: int reason; michael@0: const char * pReasonStr; michael@0: } unRegRreasonEnumPair; michael@0: michael@0: static unRegRreasonEnumPair unRegReasons[] = { michael@0: { CC_UNREG_REASON_UNSPECIFIED, "CC_UNREG_REASON_UNSPECIFIED" }, michael@0: { CC_UNREG_REASON_TCP_TIMEOUT, "CC_UNREG_REASON_TCP_TIMEOUT" }, michael@0: { CC_UNREG_REASON_CM_RESET_TCP, "CC_UNREG_REASON_CM_RESET_TCP" }, michael@0: { CC_UNREG_REASON_CM_ABORTED_TCP, "CC_UNREG_REASON_CM_ABORTED_TCP" }, michael@0: { CC_UNREG_REASON_CM_CLOSED_TCP, "C_UNREG_REASON_CM_CLOSED_TCP" }, michael@0: { CC_UNREG_REASON_REG_TIMEOUT, "CC_UNREG_REASON_REG_TIMEOUT" }, michael@0: { CC_UNREG_REASON_FALLBACK, "CC_UNREG_REASON_FALLBACK" }, michael@0: { CC_UNREG_REASON_PHONE_KEYPAD, "CC_UNREG_REASON_PHONE_KEYPAD" }, michael@0: { CC_UNREG_REASON_RESET_RESET, "CC_UNREG_REASON_RESET_RESET" }, michael@0: { CC_UNREG_REASON_RESET_RESTART, "CC_UNREG_REASON_RESET_RESTART" }, michael@0: { CC_UNREG_REASON_PHONE_REG_REJ, "CC_UNREG_REASON_PHONE_REG_REJ" }, michael@0: { CC_UNREG_REASON_PHONE_INITIALIZED, "CC_UNREG_REASON_PHONE_INITIALIZED" }, michael@0: { CC_UNREG_REASON_VOICE_VLAN_CHANGED, "CC_UNREG_REASON_VOICE_VLAN_CHANGED" }, michael@0: { CC_UNREG_REASON_POWER_SAVE_PLUS, "CC_UNREG_REASON_POWER_SAVE_PLUS" }, michael@0: { CC_UNREG_REASON_VERSION_STAMP_MISMATCH, "CC_UNREG_REASON_VERSION_STAMP_MISMATCH" }, michael@0: { CC_UNREG_REASON_VERSION_STAMP_MISMATCH_CONFIG, "CC_UNREG_REASON_VERSION_STAMP_MISMATCH_CONFIG" }, michael@0: { CC_UNREG_REASON_VERSION_STAMP_MISMATCH_SOFTKEY, "CC_UNREG_REASON_VERSION_STAMP_MISMATCH_SOFTKEY" }, michael@0: { CC_UNREG_REASON_VERSION_STAMP_MISMATCH_DIALPLAN, "CC_UNREG_REASON_VERSION_STAMP_MISMATCH_DIALPLAN" }, michael@0: { CC_UNREG_REASON_APPLY_CONFIG_RESTART, "CC_UNREG_REASON_APPLY_CONFIG_RESTART" }, michael@0: { CC_UNREG_REASON_CONFIG_RETRY_RESTART, "CC_UNREG_REASON_CONFIG_RETRY_RESTART" }, michael@0: { CC_UNREG_REASON_TLS_ERROR, "CC_UNREG_REASON_TLS_ERROR" }, michael@0: { CC_UNREG_REASON_RESET_TO_INACTIVE_PARTITION, "CC_UNREG_REASON_RESET_TO_INACTIVE_PARTITION" }, michael@0: { CC_UNREG_REASON_VPN_CONNECTIVITY_LOST, "CC_UNREG_REASON_VPN_CONNECTIVITY_LOST" } michael@0: }; michael@0: michael@0: for (int i=0; i< (int) csf_countof(unRegReasons); i++) michael@0: { michael@0: unRegRreasonEnumPair * pCurrentUnRegReasonPair = &unRegReasons[i]; michael@0: michael@0: if (pCurrentUnRegReasonPair->reason == reason) michael@0: { michael@0: CSFLogDebug( logTag, "platSetUnregReason(%s)", pCurrentUnRegReasonPair->pReasonStr); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: CSFLogError( logTag, "Unknown reason code (%d) passed to platSetUnregReason()", reason); michael@0: } michael@0: michael@0: michael@0: #endif michael@0: michael@0: #ifndef OSX michael@0: /** michael@0: * Set the kpml value for application. michael@0: * @param kpml_config the kpml value michael@0: * @return void michael@0: */ michael@0: void platSetKPMLConfig(cc_kpml_config_t kpml_config) { michael@0: } michael@0: michael@0: /** michael@0: * Check if a line has active MWI status michael@0: * @param line michael@0: * @return boolean michael@0: */ michael@0: boolean platGetMWIStatus(cc_lineid_t line) { michael@0: return TRUE; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Secure Socket API's. michael@0: * The pSIPCC expects the following Secure Socket APIs to be implemented in the michael@0: * vendor porting layer. michael@0: */ michael@0: michael@0: /** michael@0: * platSecIsServerSecure michael@0: * michael@0: * @brief Lookup the secure status of the server michael@0: * michael@0: * This function looks at the the CCM server type by using the security library michael@0: * and returns appropriate indication to the pSIPCC. michael@0: * michael@0: * michael@0: * @return Server is security enabled or not michael@0: * PLAT_SOCK_SECURE or PLAT_SOCK_NONSECURE michael@0: * michael@0: * @note This API maps to the following HandyIron API: michael@0: * int secIsServerSecure(SecServerType type) where type should be SRVR_TYPE_CCM michael@0: */ michael@0: plat_soc_status_e platSecIsServerSecure(void) { michael@0: return PLAT_SOCK_NONSECURE; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * platSecSocConnect michael@0: * @brief Securely connect to a remote server michael@0: * michael@0: * This function uses the security library APIs to connect to a remote server. michael@0: * @param[in] host server addr michael@0: * @param[in] port port number michael@0: * @param[in] ipMode IP mode to indicate v6, v4 or both michael@0: * @param[in] mode blocking connect or not michael@0: * FALSE: non-blocking; TRUE: blocking michael@0: * @param[in] tos TOS value michael@0: * @param[in] connectionType Are we talking to Call-Agent michael@0: * @param[in] connectionMode The mode of the connection michael@0: * (Authenticated/Encrypted) michael@0: * @param[out] localPort local port used for the connection michael@0: * michael@0: * @return client socket descriptor michael@0: * >=0: connected or in progress michael@0: * INVALID SOCKET: failed michael@0: * michael@0: * @pre (hostAndPort not_eq nullptr) michael@0: * @pre (localPort not_eq nullptr) michael@0: * michael@0: * @note localPort is undefined when the return value is INVALID_SOCKET michael@0: * michael@0: * @note This API maps to the HandyIron APIs as follows: michael@0: * If mode == TRUE (blocking): michael@0: * int secEstablishSecureConnection(const char* serverAddr, *uint32_t port, secConnectionType type) michael@0: * @li ipMode is UNUSED michael@0: * @li "host" maps to "serverAddr", "connectionType" maps to "type" michael@0: * @li localPort is passed in as 0 michael@0: * If mode == FALSE (non-blocking): michael@0: * int secConnect(const char* serverAddr, uint32_t port, *secConnectionType type, uint32_t localPort) michael@0: * @li ipMode is UNUSED michael@0: * @li "host" maps to "serverAddr", "connectionType" maps to "type" michael@0: * michael@0: * @note The implementation should use the "setsockopt" to set the "tos" value passed michael@0: * in this API on the created socket. michael@0: * michael@0: */ michael@0: cpr_socket_t michael@0: platSecSocConnect (char *host, michael@0: int port, michael@0: int ipMode, michael@0: boolean mode, michael@0: unsigned int tos, michael@0: plat_soc_connect_mode_e connectionMode, michael@0: uint16_t *localPort) { michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * platSecSockIsConnected michael@0: * Determine the status of a secure connection that was initiated michael@0: * in non-blocking mode michael@0: * michael@0: * @param[in] sock socket descriptor michael@0: * michael@0: * @return connection status michael@0: * @li connection complete: PLAT_SOCK_CONN_OK michael@0: * @li connection waiting: PLAT_SOCK_CONN_WAITING michael@0: * @li connection failed: PLAT_SOCK_CONN_FAILED michael@0: * michael@0: * @note This API maps to the following HandyIron API: michael@0: * int secIsConnectionReady (int connDesc) michael@0: * The "sock" is the connection descriptor. michael@0: */ michael@0: plat_soc_connect_status_e platSecSockIsConnected (cpr_socket_t sock) { michael@0: return PLAT_SOCK_CONN_OK; michael@0: } michael@0: #endif //#endif !OSX michael@0: michael@0: /** michael@0: * platGenerateCryptoRand michael@0: * @brief Generates a Random Number michael@0: * michael@0: * Generate crypto graphically random number for a desired length. michael@0: * The function is expected to be much slower than the cpr_rand(). michael@0: * This function should be used when good random number is needed michael@0: * such as random number that to be used for SRTP key for an example. michael@0: * michael@0: * @param[in] buf - pointer to the buffer to store the result of random michael@0: * bytes requested. michael@0: * @param[in] len - pointer to the length of the desired random bytes. michael@0: * When calling the function, the integer's value michael@0: * should be set to the desired number of random michael@0: * bytes ('buf' should be of at least this size). michael@0: * upon success, its value will be set to the michael@0: * actual number of random bytes being returned. michael@0: * (realistically, there is a maximum number of michael@0: * random bytes that can be returned at a time. michael@0: * if the caller request more than that, the michael@0: * 'len' will indicate how many bytes are actually being michael@0: * returned) on failure, its value will be set to 0. michael@0: * michael@0: * @return michael@0: * 1 - success. michael@0: * 0 - fail. michael@0: * michael@0: * @note The intent of this function is to generate a cryptographically strong michael@0: * random number. Vendors can map this to HandyIron or OpenSSL random number michael@0: * generation functions. michael@0: * michael@0: * @note This API maps to the following HandyIron API: michael@0: * int secGetRandomData(uint8_t *buf, uint32_t size). Also note that a michael@0: * "secAddEntropy(...)" may be required the first time to feed entropy data to michael@0: * the random number generator. michael@0: */ michael@0: #ifdef LINUX michael@0: int platGenerateCryptoRand(uint8_t *buf, int *len) { michael@0: int fd; michael@0: int rc = 0; michael@0: ssize_t s; michael@0: michael@0: if ((fd = open("/dev/urandom", O_RDONLY)) == -1) { michael@0: CSFLogDebug( logTag, "Failed to open prng driver"); michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * Try to read the given amount of bytes from the PRNG device. We do not michael@0: * handle short reads but just return the number of bytes read from the michael@0: * device. The caller has to manage this. michael@0: * E.g. gsmsdp_generate_key() in core/gsm/gsm_sdp_crypto.c michael@0: */ michael@0: michael@0: s = read(fd, buf, (size_t) *len); michael@0: michael@0: if (s > 0) { michael@0: *len = s; michael@0: rc = 1; /* Success */ michael@0: } else { michael@0: *len = 0; michael@0: rc = 0; /* Failure */ michael@0: } michael@0: michael@0: michael@0: (void) close(fd); michael@0: return rc; michael@0: } michael@0: #else michael@0: int platGenerateCryptoRand(uint8_t *buf, int *len) { michael@0: return 0; michael@0: } michael@0: #endif//!LINUX michael@0: michael@0: #ifndef OSX michael@0: michael@0: /* michael@0: The version is to regulate certain features like Join, which are added in later releases. michael@0: This is exchanged during registration with CUCM. It is a sheer luck that join may be working in some cases. michael@0: In this case Set API you have to store each of those API and return the same value in get. michael@0: michael@0: We can go over all platform API’ and see which one are mandatory, we can do this in an email or in a meeting. michael@0: Please send out list methods for which you need clarification. michael@0: */ michael@0: static cc_uint32_t majorSIS=1, minorSIS=0, addtnlSIS =0; michael@0: static char sis_ver_name[CC_MAX_LEN_REQ_SUPP_PARAM_CISCO_SISTAG] = {0}; michael@0: michael@0: /** michael@0: * Sets the SIS protocol version michael@0: * michael@0: * @param a - major version michael@0: * @param b - minor version michael@0: * @param c - additional version information michael@0: * @param name - version name michael@0: * michael@0: * @return void michael@0: * @note the platform should store this information and provide it when asked via the platGetSISProtocolVer() michael@0: */ michael@0: void platSetSISProtocolVer(cc_uint32_t a, cc_uint32_t b, cc_uint32_t c, char* name) { michael@0: majorSIS = a; michael@0: minorSIS = b; michael@0: addtnlSIS = c; michael@0: michael@0: if (name) { michael@0: sstrncpy(sis_ver_name, name, csf_countof(sis_ver_name)); michael@0: } else { michael@0: *sis_ver_name = '\0'; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Provides the SIS protocol version michael@0: * michael@0: * @param *a pointer to fill in the major version michael@0: * @param *b pointer to fill in the minor version michael@0: * @param *c pointer to fill in the additonal version michael@0: * @param *name pointer to fill in the version name michael@0: * michael@0: * @return void michael@0: */ michael@0: void michael@0: platGetSISProtocolVer (cc_uint32_t *a, cc_uint32_t *b, cc_uint32_t *c, char* name) { michael@0: michael@0: if (a != nullptr) michael@0: { michael@0: *a = majorSIS; michael@0: } michael@0: michael@0: if (b != nullptr) michael@0: { michael@0: *b = minorSIS; michael@0: } michael@0: michael@0: if (c != nullptr) michael@0: { michael@0: *c = addtnlSIS; michael@0: } michael@0: michael@0: if (name != nullptr) michael@0: { michael@0: sstrncpy(name, sis_ver_name, CC_MAX_LEN_REQ_SUPP_PARAM_CISCO_SISTAG); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: void debug_bind_keyword(const char *cmd, int32_t *flag_ptr) { michael@0: return; michael@0: } michael@0: michael@0: void debugif_add_keyword(const char *x, const char *y) { michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: // Keep this when we are building the sipcc library without it, so that we capture logging michael@0: int debugif_printf(const char *_format, ...) { michael@0: va_list ap; michael@0: va_start(ap, _format); michael@0: CSFLogDebugV( logTag, _format, ap); michael@0: va_end(ap); michael@0: return 1; // Fake a "happy" return value. michael@0: } michael@0: michael@0: } michael@0: