diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index e6709f0950..0c28f2448a 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -1273,6 +1273,89 @@ static void iax2_lock_owner(int callno) } } +/*! + * \internal + * \brief Check if a control subtype is allowed on the wire. + * + * \param subtype Control frame subtype to check if allowed to/from the wire. + * + * \retval non-zero if allowed. + */ +static int iax2_is_control_frame_allowed(int subtype) +{ + enum ast_control_frame_type control = subtype; + int is_allowed; + + /* + * Note: If we compare the enumeration type, which does not have any + * negative constants, the compiler may optimize this code away. + * Therefore, we must perform an integer comparison here. + */ + if (subtype == -1) { + return -1; + } + + /* Default to not allowing control frames to pass. */ + is_allowed = 0; + + /* + * The switch default is not present in order to take advantage + * of the compiler complaining of a missing enum case. + */ + switch (control) { + /* + * These control frames make sense to send/receive across the link. + */ + case AST_CONTROL_HANGUP: + case AST_CONTROL_RING: + case AST_CONTROL_RINGING: + case AST_CONTROL_ANSWER: + case AST_CONTROL_BUSY: + case AST_CONTROL_TAKEOFFHOOK: + case AST_CONTROL_OFFHOOK: + case AST_CONTROL_CONGESTION: + case AST_CONTROL_FLASH: + case AST_CONTROL_WINK: + case AST_CONTROL_OPTION: + case AST_CONTROL_RADIO_KEY: + case AST_CONTROL_RADIO_UNKEY: + case AST_CONTROL_PROGRESS: + case AST_CONTROL_PROCEEDING: + case AST_CONTROL_HOLD: + case AST_CONTROL_UNHOLD: + case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_CONNECTED_LINE: + case AST_CONTROL_REDIRECTING: + case AST_CONTROL_T38_PARAMETERS: + case AST_CONTROL_AOC: + case AST_CONTROL_INCOMPLETE: + is_allowed = -1; + break; + + /* + * These control frames do not make sense to send/receive across the link. + */ + case _XXX_AST_CONTROL_T38: + /* The control value is deprecated in favor of AST_CONTROL_T38_PARAMETERS. */ + case AST_CONTROL_SRCUPDATE: + /* Across an IAX link the source is still the same. */ + case AST_CONTROL_TRANSFER: + /* A success/fail status report from calling ast_transfer() on this machine. */ + case AST_CONTROL_CC: + /* The payload contains pointers that are valid for the sending machine only. */ + case AST_CONTROL_SRCCHANGE: + /* Across an IAX link the source is still the same. */ + case AST_CONTROL_READ_ACTION: + /* The action can only be done by the sending machine. */ + case AST_CONTROL_END_OF_Q: + /* This frame would cause the call to unexpectedly hangup. */ + case AST_CONTROL_UPDATE_RTP_PEER: + /* Only meaningful across a bridge on this machine for direct-media exchange. */ + break; + } + return is_allowed; +} + static void mwi_event_cb(const struct ast_event *event, void *userdata) { /* The MWI subscriptions exist just so the core knows we care about those @@ -5688,8 +5771,13 @@ static int iax2_indicate(struct ast_channel *c, int condition, const void *data, } break; case AST_CONTROL_CONNECTED_LINE: - if (!ast_test_flag64(pvt, IAX_SENDCONNECTEDLINE)) + case AST_CONTROL_REDIRECTING: + if (!ast_test_flag64(pvt, IAX_SENDCONNECTEDLINE)) { + /* We are not configured to allow sending these updates. */ + ast_debug(2, "Callno %u: Config blocked sending control frame %d.\n", + callno, condition); goto done; + } break; } @@ -7517,6 +7605,12 @@ static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsig static int send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno) { + if (type == AST_FRAME_CONTROL && !iax2_is_control_frame_allowed(command)) { + /* Control frame should not go out on the wire. */ + ast_debug(2, "Callno %u: Blocked sending control frame %d.\n", + i->callno, command); + return 0; + } return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0); } @@ -10435,13 +10529,6 @@ static int socket_process(struct iax2_thread *thread) iaxs[fr->callno]->videoformat = f.subclass.codec & ~0x1LL; } } - if (f.frametype == AST_FRAME_CONTROL && iaxs[fr->callno]->owner) { - if (f.subclass.integer == AST_CONTROL_BUSY) { - iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_BUSY; - } else if (f.subclass.integer == AST_CONTROL_CONGESTION) { - iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_CONGESTION; - } - } if (f.frametype == AST_FRAME_IAX) { ast_sched_thread_del(sched, iaxs[fr->callno]->initid); /* Handle the IAX pseudo frame itself */ @@ -11641,17 +11728,48 @@ immediatedial: ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } - /* Don't allow connected line updates unless we are configured to */ - if (f.frametype == AST_FRAME_CONTROL && f.subclass.integer == AST_CONTROL_CONNECTED_LINE) { - struct ast_party_connected_line connected; - if (!ast_test_flag64(iaxs[fr->callno], IAX_RECVCONNECTEDLINE)) { + if (f.frametype == AST_FRAME_CONTROL) { + if (!iax2_is_control_frame_allowed(f.subclass.integer)) { + /* Control frame not allowed to come from the wire. */ + ast_debug(2, "Callno %u: Blocked receiving control frame %d.\n", + fr->callno, f.subclass.integer); ast_variables_destroy(ies.vars); ast_mutex_unlock(&iaxsl[fr->callno]); return 1; } + if (f.subclass.integer == AST_CONTROL_CONNECTED_LINE + || f.subclass.integer == AST_CONTROL_REDIRECTING) { + if (!ast_test_flag64(iaxs[fr->callno], IAX_RECVCONNECTEDLINE)) { + /* We are not configured to allow receiving these updates. */ + ast_debug(2, "Callno %u: Config blocked receiving control frame %d.\n", + fr->callno, f.subclass.integer); + ast_variables_destroy(ies.vars); + ast_mutex_unlock(&iaxsl[fr->callno]); + return 1; + } + } - /* Initialize defaults */ + iax2_lock_owner(fr->callno); + if (iaxs[fr->callno] && iaxs[fr->callno]->owner) { + if (f.subclass.integer == AST_CONTROL_BUSY) { + iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_BUSY; + } else if (f.subclass.integer == AST_CONTROL_CONGESTION) { + iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_CONGESTION; + } + ast_channel_unlock(iaxs[fr->callno]->owner); + } + } + + if (f.frametype == AST_FRAME_CONTROL + && f.subclass.integer == AST_CONTROL_CONNECTED_LINE) { + struct ast_party_connected_line connected; + + /* + * Process a received connected line update. + * + * Initialize defaults. + */ ast_party_connected_line_init(&connected); connected.id.number.presentation = iaxs[fr->callno]->calling_pres; connected.id.name.presentation = iaxs[fr->callno]->calling_pres; @@ -11674,6 +11792,7 @@ immediatedial: } ast_party_connected_line_free(&connected); } + /* Common things */ f.src = "IAX2"; f.mallocd = 0; diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample index 76eac0714f..55a13201c3 100644 --- a/configs/iax.conf.sample +++ b/configs/iax.conf.sample @@ -530,14 +530,18 @@ host=216.207.245.47 ; suggested to the other side as well if it is for example a phone instead of ; another PBX. ; -;connectedline=yes ; Set how connected line information is handled for this -; ; peer. If set to "yes", both sending and receiving -; ; connected line information will be enabled. If set to -; ; "send", this peer will send connected line information -; ; but will not process connected line updates. If set to -; ; "receive", connected line updates will be processed -; ; but not sent. If set to "no", connected line updates -; ; will be disabled. Default is "no". +;connectedline=yes ; Set if connected line and redirecting information updates +; ; are passed between Asterisk servers for this peer. +; ; yes - Sending and receiving updates are enabled. +; ; send - Only send updates. +; ; receive - Only process received updates. +; ; no - Sending and receiving updates are disabled. +; ; Default is "no". +; ; +; ; Note: Because of an incompatibility between Asterisk v1.4 +; ; and Asterisk v1.8 or later, this option must be set +; ; to "no" toward the Asterisk v1.4 peer. A symptom of the +; ; incompatibility is the call gets disconnected unexpectedly. ;[dynamichost] diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 9cabfdf24d..cf1ca544c7 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -304,6 +304,18 @@ extern struct ast_frame ast_null_frame; /*! Reserved bit - do not use */ #define AST_FORMAT_RESERVED (1ULL << 63) +/*! + * \brief Internal control frame subtype field values. + * + * \warning + * IAX2 sends these values out over the wire. To prevent future + * incompatibilities, pick the next value in the enum from whatever + * is on the current trunk. If you lose the merge race you need to + * fix the previous branches to match what is on trunk. In addition + * you need to change chan_iax2 to explicitly allow the control + * frame over the wire if it makes sense for the frame to be passed + * to another Asterisk instance. + */ enum ast_control_frame_type { AST_CONTROL_HANGUP = 1, /*!< Other end has hungup */ AST_CONTROL_RING = 2, /*!< Local ring */ @@ -335,7 +347,21 @@ enum ast_control_frame_type { AST_CONTROL_AOC = 28, /*!< Advice of Charge with encoded generic AOC payload */ AST_CONTROL_END_OF_Q = 29, /*!< Indicate that this position was the end of the channel queue for a softhangup. */ AST_CONTROL_INCOMPLETE = 30, /*!< Indication that the extension dialed is incomplete */ - AST_CONTROL_UPDATE_RTP_PEER = 31, /*!< Interrupt the bridge and have it update the peer */ + AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */ + + /* + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * IAX2 sends these values out over the wire. To prevent future + * incompatibilities, pick the next value in the enum from whatever + * is on the current trunk. If you lose the merge race you need to + * fix the previous branches to match what is on trunk. In addition + * you need to change chan_iax2 to explicitly allow the control + * frame over the wire if it makes sense for the frame to be passed + * to another Asterisk instance. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ }; enum ast_frame_read_action {