}
}
+/*!
+ * \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:
+ case AST_CONTROL_MCID:
+ 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. */
+ case AST_CONTROL_PVT_CAUSE_CODE:
+ /* Intended only for the sending machine's local channel structure. */
+ 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
}
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;
case AST_CONTROL_PVT_CAUSE_CODE:
res = -1;
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);
}
iaxs[fr->callno]->videoformat = ast_format_to_old_bitfield(&f.subclass.format);
}
}
- if (f.frametype == AST_FRAME_CONTROL && iaxs[fr->callno]->owner) {
- if (f.subclass.integer == AST_CONTROL_BUSY) {
- ast_channel_hangupcause_set(iaxs[fr->callno]->owner, AST_CAUSE_BUSY);
- } else if (f.subclass.integer == AST_CONTROL_CONGESTION) {
- ast_channel_hangupcause_set(iaxs[fr->callno]->owner, AST_CAUSE_CONGESTION);
- }
- }
if (f.frametype == AST_FRAME_IAX) {
AST_SCHED_DEL(sched, iaxs[fr->callno]->initid);
/* Handle the IAX pseudo frame itself */
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;
+ }
+ }
+
+ iax2_lock_owner(fr->callno);
+ if (iaxs[fr->callno] && iaxs[fr->callno]->owner) {
+ if (f.subclass.integer == AST_CONTROL_BUSY) {
+ ast_channel_hangupcause_set(iaxs[fr->callno]->owner, AST_CAUSE_BUSY);
+ } else if (f.subclass.integer == AST_CONTROL_CONGESTION) {
+ ast_channel_hangupcause_set(iaxs[fr->callno]->owner, 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;
- /* Initialize defaults */
+ /*
+ * 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;
}
ast_party_connected_line_free(&connected);
}
+
/* Common things */
f.src = "IAX2";
f.mallocd = 0;
/*! Reject link request */
#define AST_HTML_LINKREJECT 20
+/*!
+ * \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 */
AST_CONTROL_MCID = 31, /*!< Indicate that the caller is being malicious. */
AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */
AST_CONTROL_PVT_CAUSE_CODE = 33, /*!< Contains an update to the protocol-specific cause-code stored for branching dials */
+
+ /*
+ * 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 {