diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index 3e1748d6e2..677c703b39 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -623,6 +623,26 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, return skinny_send_reply_quiet(listener, message); } +switch_status_t perform_send_srvreq_response(listener_t *listener, + const char *file, const char *func, int line, + char *ip, uint32_t port) +{ + skinny_message_t *message; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.serv_res_mess)); + message->type = SERVER_RESPONSE_MESSAGE; + message->length = 4 + sizeof(message->data.serv_res_mess); + + message->data.serv_res_mess.serverListenPort[0] = port; + switch_inet_pton(AF_INET,ip, &message->data.serv_res_mess.serverIpAddr[0]); + switch_copy_string(message->data.serv_res_mess.server[0].serverName,ip,sizeof(message->data.serv_res_mess.server[0].serverName)); + + skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, + "Sending Server Request Response with IP (%s) and Port (%d)\n", ip, port); + + return skinny_send_reply(listener, message); +} + switch_status_t perform_send_start_media_transmission(listener_t *listener, const char *file, const char *func, int line, uint32_t conference_id, diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index 5127b1b54b..9bdd584b69 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -36,6 +36,53 @@ /* mod_skinny.h should be loaded first */ #include "mod_skinny.h" +/*****************************************************************************/ +/* SKINNY TYPES */ +/*****************************************************************************/ +typedef enum { + SKINNY_CODEC_NONE = 0, + SKINNY_CODEC_NONSTANDARD = 1, + SKINNY_CODEC_ALAW_64K = 2, + SKINNY_CODEC_ALAW_56K = 3, + SKINNY_CODEC_ULAW_64K = 4, + SKINNY_CODEC_ULAW_56K = 5, + SKINNY_CODEC_G722_64K = 6, + SKINNY_CODEC_G722_56K = 7, + SKINNY_CODEC_G722_48K = 8, + SKINNY_CODEC_G723_1 = 9, + SKINNY_CODEC_G728 = 10, + SKINNY_CODEC_G729 = 11, + SKINNY_CODEC_G729A = 12, + SKINNY_CODEC_IS11172 = 13, + SKINNY_CODEC_IS13818 = 14, + SKINNY_CODEC_G729B = 15, + SKINNY_CODEC_G729AB = 16, + SKINNY_CODEC_GSM_FULL = 18, + SKINNY_CODEC_GSM_HALF = 19, + SKINNY_CODEC_GSM_EFULL = 20, + SKINNY_CODEC_WIDEBAND_256K = 25, + SKINNY_CODEC_DATA_64K = 32, + SKINNY_CODEC_DATA_56K = 33, + SKINNY_CODEC_G722_1_32K = 40, + SKINNY_CODEC_G722_1_24K = 41, + SKINNY_CODEC_GSM = 80, + SKINNY_CODEC_ACTIVEVOICE = 81, + SKINNY_CODEC_G726_32K = 82, + SKINNY_CODEC_G726_24K = 83, + SKINNY_CODEC_G726_16K = 84, + SKINNY_CODEC_G729B_BIS = 85, + SKINNY_CODEC_G729B_LOW = 86, + SKINNY_CODEC_H261 = 100, + SKINNY_CODEC_H263 = 101, + SKINNY_CODEC_VIDEO = 102, + SKINNY_CODEC_H264 = 103, + SKINNY_CODEC_T120 = 105, + SKINNY_CODEC_H224 = 106, + SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 +} skinny_codecs; + +char* skinny_codec2string(skinny_codecs skinnycodec); + /*****************************************************************************/ /* SKINNY MESSAGE DATA */ /*****************************************************************************/ @@ -211,6 +258,145 @@ struct PACKED data_message { #define DEVICE_UPDATECAPABILITIES 0x0030 +#define MAX_CUSTOM_PICTURES 6 +#define MAX_LAYOUT_WITH_SAME_SERVICE 5 +#define MAX_SERVICE_TYPE 4 +#define SKINNY_MAX_CAPABILITIES 18 /*!< max capabilities allowed in Cap response message */ +#define SKINNY_MAX_VIDEO_CAPABILITIES 10 +#define SKINNY_MAX_DATA_CAPABILITIES 5 +#define MAX_LEVEL_PREFERENCE 4 + +/*! + * \brief Picture Format Structure + */ +typedef struct { + uint32_t customPictureFormatWidth; /*!< Picture Width */ + uint32_t customPictureFormatHeight; /*!< Picture Height */ + uint32_t customPictureFormatpixelAspectRatio; /*!< Picture Pixel Aspect Ratio */ + uint32_t customPictureFormatpixelclockConversionCode; /*!< Picture Pixel Conversion Code */ + uint32_t customPictureFormatpixelclockDivisor; /*!< Picture Pixel Divisor */ +} customPictureFormat_t; + + +/*! + * \brief Video Level Preference Structure + */ +typedef struct { + uint32_t transmitPreference; /*!< Transmit Preference */ + uint32_t format; /*!< Format / Codec */ + uint32_t maxBitRate; /*!< Maximum BitRate */ + uint32_t minBitRate; /*!< Minimum BitRate */ + uint32_t MPI; /*!< */ + uint32_t serviceNumber; /*!< Service Number */ +} levelPreference_t; /*!< Level Preference Structure */ + +/*! + * \brief Layout Config Structure (Update Capabilities Message Struct) + * \since 20080111 + */ +typedef struct { + uint32_t layout; /*!< Layout \todo what is layout? */ +} layoutConfig_t; /*!< Layout Config Structure */ + + +/*! + * \brief Service Resource Structure + */ +typedef struct { + uint32_t layoutCount; /*!< Layout Count */ + layoutConfig_t layout[MAX_LAYOUT_WITH_SAME_SERVICE]; /*!< Layout */ + uint32_t serviceNum; /*!< Service Number */ + uint32_t maxStreams; /*!< Maximum number of Streams */ + uint32_t maxConferences; /*!< Maximum number of Conferences */ + uint32_t activeConferenceOnRegistration; /*!< Active Conference On Registration */ +} serviceResource_t; + + + +/*! + * \brief Audio Capabilities Structure + */ +typedef struct { + skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */ + uint32_t lel_maxFramesPerPacket; /*!< Maximum Number of Frames per IP Packet */ + uint32_t lel_unknown[2]; /*!< this are related to G.723 */ +} audioCap_t; + +/*! + * \brief Video Capabilities Structure + */ +typedef struct { + skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */ + uint32_t lel_transmitOreceive; /*!< Transmit of Receive */ + uint32_t lel_levelPreferenceCount; /*!< Level of Preference Count */ + + levelPreference_t levelPreference[MAX_LEVEL_PREFERENCE]; /*!< Level Preference */ + +// uint32_t lel_codec_options[2]; /*!< Codec Options */ + + union { + struct { + uint32_t unknown1; + uint32_t unknown2; + } h263; + struct { + uint32_t profile; /*!< H264 profile */ + uint32_t level; /*!< H264 level */ + } h264; + } codec_options; + + /** + * Codec options contains data specific for every codec + * + * Here is a list of known parameters per codec + // H.261 + uint32_t lel_temporalSpatialTradeOffCapability; + uint32_t lel_stillImageTransmission; + + // H.263 + uint32_t lel_h263_capability_bitfield; + uint32_t lel_annexNandWFutureUse; + + // Video + uint32_t lel_modelNumber; + uint32_t lel_bandwidth; + */ +} videoCap_t; /*!< Video Capabilities Structure */ + +/*! + * \brief Data Capabilities Structure + */ +typedef struct { + uint32_t payloadCapability; /*!< Payload Capability */ + uint32_t transmitOrReceive; /*!< Transmit or Receive */ + uint32_t protocolDependentData; /*!< Protocol Dependent Data */ + uint32_t maxBitRate; /*!< Maximum BitRate */ +} dataCap_t; /*!< Data Capabilities Structure */ + + +struct PACKED update_capabilities_message { + uint32_t lel_audioCapCount; /*!< Audio Capability Count */ + uint32_t lel_videoCapCount; /*!< Video Capability Count */ + uint32_t lel_dataCapCount; /*!< Data Capability Count */ + uint32_t RTPPayloadFormat; /*!< RTP Payload Format */ + uint32_t customPictureFormatCount; /*!< Custom Picture Format Count */ + + customPictureFormat_t customPictureFormat[MAX_CUSTOM_PICTURES]; /*!< Custom Picture Format */ + + uint32_t activeStreamsOnRegistration; /*!< Active Streams on Registration */ + uint32_t maxBW; /*!< Max BW ?? */ + + uint32_t serviceResourceCount; /*!< Service Resource Count */ + serviceResource_t serviceResource[MAX_SERVICE_TYPE]; /*!< Service Resource */ + + audioCap_t audioCaps[SKINNY_MAX_CAPABILITIES]; /*!< Audio Capabilities */ + videoCap_t videoCaps[SKINNY_MAX_VIDEO_CAPABILITIES]; /*!< Video Capabilities */ + dataCap_t dataCaps[SKINNY_MAX_DATA_CAPABILITIES]; /*!< Data Capabilities */ + + uint32_t unknown; /*!< Unknown */ +}; + + /* ServiceUrlStatReqMessage */ #define SERVICE_URL_STAT_REQ_MESSAGE 0x0033 struct PACKED service_url_stat_req_message { @@ -447,6 +633,21 @@ struct PACKED register_reject_message { }; #define SERVER_RESPONSE_MESSAGE 0x009E +#define ServerMaxNameSize 48 +#define StationMaxServers 5 +/*! + * \brief Station Identifier Structure + */ +typedef struct { + char serverName[ServerMaxNameSize]; /*!< Server Name */ +} ServerIdentifier; + +struct PACKED server_response_message { + ServerIdentifier server[StationMaxServers]; /*!< Server Identifier */ + uint32_t serverListenPort[StationMaxServers]; /*!< Server is Listening on Port */ + uint32_t serverIpAddr[StationMaxServers]; /*!< Server IP Port */ +}; /*!< Server Result Message Structure */ + /* ResetMessage */ #define RESET_MESSAGE 0x009F @@ -676,6 +877,7 @@ union skinny_data { /* no data for CAPABILITIES_REQ_MESSAGE */ struct register_reject_message reg_rej; struct reset_message reset; + struct server_response_message serv_res_mess; /* no data for KEEP_ALIVE_ACK_MESSAGE */ struct open_receive_channel_message open_receive_channel; struct close_receive_channel_message close_receive_channel; @@ -696,6 +898,7 @@ union skinny_data { /* see field "extended_data" for USER_TO_DEVICE_DATA_VERSION1_MESSAGE */ struct dialed_phone_book_ack_message dialed_phone_book_ack; + struct update_capabilities_message upd_cap; struct data_message data; struct extended_data_message extended_data; @@ -725,52 +928,7 @@ struct PACKED skinny_message { typedef struct skinny_message skinny_message_t; -/*****************************************************************************/ -/* SKINNY TYPES */ -/*****************************************************************************/ -typedef enum { - SKINNY_CODEC_NONE = 0, - SKINNY_CODEC_NONSTANDARD = 1, - SKINNY_CODEC_ALAW_64K = 2, - SKINNY_CODEC_ALAW_56K = 3, - SKINNY_CODEC_ULAW_64K = 4, - SKINNY_CODEC_ULAW_56K = 5, - SKINNY_CODEC_G722_64K = 6, - SKINNY_CODEC_G722_56K = 7, - SKINNY_CODEC_G722_48K = 8, - SKINNY_CODEC_G723_1 = 9, - SKINNY_CODEC_G728 = 10, - SKINNY_CODEC_G729 = 11, - SKINNY_CODEC_G729A = 12, - SKINNY_CODEC_IS11172 = 13, - SKINNY_CODEC_IS13818 = 14, - SKINNY_CODEC_G729B = 15, - SKINNY_CODEC_G729AB = 16, - SKINNY_CODEC_GSM_FULL = 18, - SKINNY_CODEC_GSM_HALF = 19, - SKINNY_CODEC_GSM_EFULL = 20, - SKINNY_CODEC_WIDEBAND_256K = 25, - SKINNY_CODEC_DATA_64K = 32, - SKINNY_CODEC_DATA_56K = 33, - SKINNY_CODEC_G722_1_32K = 40, - SKINNY_CODEC_G722_1_24K = 41, - SKINNY_CODEC_GSM = 80, - SKINNY_CODEC_ACTIVEVOICE = 81, - SKINNY_CODEC_G726_32K = 82, - SKINNY_CODEC_G726_24K = 83, - SKINNY_CODEC_G726_16K = 84, - SKINNY_CODEC_G729B_BIS = 85, - SKINNY_CODEC_G729B_LOW = 86, - SKINNY_CODEC_H261 = 100, - SKINNY_CODEC_H263 = 101, - SKINNY_CODEC_VIDEO = 102, - SKINNY_CODEC_H264 = 103, - SKINNY_CODEC_T120 = 105, - SKINNY_CODEC_H224 = 106, - SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 -} skinny_codecs; -char* skinny_codec2string(skinny_codecs skinnycodec); /*****************************************************************************/ /* SKINNY FUNCTIONS */ @@ -862,6 +1020,11 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, uint32_t mode); #define send_set_speaker_mode(listener, ...) perform_send_set_speaker_mode(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__) +switch_status_t perform_send_srvreq_response(listener_t *listener, + const char *file, const char *func, int line, + char *ip, uint32_t port); +#define send_srvreq_response(listener, ...) perform_send_srvreq_response(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__) + switch_status_t perform_send_start_media_transmission(listener_t *listener, const char *file, const char *func, int line, uint32_t conference_id, diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 010e5b2379..8a0c18ebd8 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -2183,6 +2183,79 @@ switch_status_t skinny_handle_accessory_status_message(listener_t *listener, ski return SWITCH_STATUS_SUCCESS; } +switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_message_t *request) +{ + char *sql; + skinny_profile_t *profile; + + uint32_t i = 0; + uint32_t n = 0; + char *codec_order[SWITCH_MAX_CODECS]; + char *codec_string; + + size_t string_len, string_pos, pos; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount)); + + n = request->data.upd_cap.lel_audioCapCount; + if (n > SWITCH_MAX_CODECS) { + n = SWITCH_MAX_CODECS; + } + string_len = -1; + + skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount) + n * sizeof(request->data.upd_cap.audioCaps[0])); + + for (i = 0; i < n; i++) { + char *codec = skinny_codec2string(request->data.upd_cap.audioCaps[i].lel_payloadCapability); + codec_order[i] = codec; + string_len += strlen(codec)+1; + } + i = 0; + pos = 0; + codec_string = switch_core_alloc(listener->pool, string_len+1); + for (string_pos = 0; string_pos < string_len; string_pos++) { + char *codec = codec_order[i]; + switch_assert(i < n); + if(pos == strlen(codec)) { + codec_string[string_pos] = ','; + i++; + pos = 0; + } else { + codec_string[string_pos] = codec[pos++]; + } + } + codec_string[string_len] = '\0'; + if ((sql = switch_mprintf( + "UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'", + codec_string, + listener->device_name + ))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Codecs %s supported.\n", codec_string); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t skinny_handle_server_req_message(listener_t *listener, skinny_message_t *request) +{ + skinny_profile_t *profile; + + profile = listener->profile; + + skinny_log_l(listener, SWITCH_LOG_INFO, "Received Server Request Message (length=%d).\n", request->length); + + send_srvreq_response(listener, profile->ip, profile->port); + return SWITCH_STATUS_SUCCESS; +} + switch_status_t skinny_handle_xml_alarm(listener_t *listener, skinny_message_t *request) { switch_event_t *event = NULL; @@ -2208,7 +2281,7 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re skinny_log_l(listener, SWITCH_LOG_DEBUG, "Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); } - if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE) { + if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE && request->type != KEEP_ALIVE_MESSAGE) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Device should send a register message first. Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); return SWITCH_STATUS_FALSE; @@ -2280,6 +2353,10 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re return skinny_handle_accessory_status_message(listener, request); case XML_ALARM_MESSAGE: return skinny_handle_xml_alarm(listener, request); + case DEVICE_UPDATECAPABILITIES: + return skinny_handle_updatecapabilities(listener, request); + case SERVER_REQ_MESSAGE: + return skinny_handle_server_req_message(listener, request); default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length);