diff options
author | bbryant <bbryant@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-09-09 18:51:52 +0000 |
---|---|---|
committer | bbryant <bbryant@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-09-09 18:51:52 +0000 |
commit | 722eb3c4c3cfa1c0cee915c949c5f95199ee24dd (patch) | |
tree | 25683963c5e51bdedd6211cd0ea92a85639505c3 /res | |
parent | 815b5b09da5e555add7bba3d8fca588e7611248a (diff) |
Merged revisions 285710 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.6.2
........
r285710 | bbryant | 2010-09-09 14:50:13 -0400 (Thu, 09 Sep 2010) | 8 lines
Fixes an issue with dialplan pattern matching where the specificity for pattern ranges and pattern special characters was inconsistent.
(closes issue #16903)
Reported by: Nick_Lewis
Patches:
pbx.c-specificity.patch uploaded by Nick Lewis (license 657)
Tested by: Nick_Lewis
........
git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.8@285711 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'res')
-rw-r--r-- | res/res_ais.c | 20 | ||||
-rw-r--r-- | res/res_calendar.c | 22 | ||||
-rw-r--r-- | res/res_calendar_caldav.c | 14 | ||||
-rw-r--r-- | res/res_calendar_ews.c | 82 | ||||
-rw-r--r-- | res/res_calendar_icalendar.c | 8 | ||||
-rw-r--r-- | res/res_config_odbc.c | 2 | ||||
-rw-r--r-- | res/res_config_pgsql.c | 16 | ||||
-rw-r--r-- | res/res_fax.c | 128 | ||||
-rw-r--r-- | res/res_jabber.c | 23 | ||||
-rw-r--r-- | res/res_musiconhold.c | 124 | ||||
-rw-r--r-- | res/res_odbc.c | 3 | ||||
-rw-r--r-- | res/res_pktccops.c | 35 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 21 | ||||
-rw-r--r-- | res/res_srtp.c | 96 | ||||
-rw-r--r-- | res/res_stun_monitor.c | 311 |
15 files changed, 786 insertions, 119 deletions
diff --git a/res/res_ais.c b/res/res_ais.c index adb3290e1..9bcceeade 100644 --- a/res/res_ais.c +++ b/res/res_ais.c @@ -113,9 +113,9 @@ const char *ais_err2str(SaAisErrorT error) static void *dispatch_thread_handler(void *data) { - SaSelectionObjectT clm_fd, evt_fd, max_fd; + SaSelectionObjectT clm_fd, evt_fd; int res; - fd_set read_fds; + struct pollfd pfd[2] = { { .events = POLLIN, }, { .events = POLLIN, } }; SaAisErrorT ais_res; ais_res = saClmSelectionObjectGet(clm_handle, &clm_fd); @@ -132,24 +132,26 @@ static void *dispatch_thread_handler(void *data) return NULL; } - max_fd = clm_fd > evt_fd ? clm_fd : evt_fd; + pfd[0].fd = clm_fd; + pfd[1].fd = evt_fd; while (!dispatch_thread.stop) { - FD_ZERO(&read_fds); - FD_SET(clm_fd, &read_fds); - FD_SET(evt_fd, &read_fds); + pfd[0].revents = 0; + pfd[1].revents = 0; - res = ast_select(max_fd + 1, &read_fds, NULL, NULL, NULL); + res = ast_poll(pfd, 2, -1); if (res == -1 && errno != EINTR && errno != EAGAIN) { ast_log(LOG_ERROR, "Select error (%s) dispatch thread going away now, " "and the module will no longer operate.\n", strerror(errno)); break; } - if (FD_ISSET(clm_fd, &read_fds)) + if (pfd[0].revents & POLLIN) { saClmDispatch(clm_handle, SA_DISPATCH_ALL); - if (FD_ISSET(evt_fd, &read_fds)) + } + if (pfd[1].revents & POLLIN) { saEvtDispatch(evt_handle, SA_DISPATCH_ALL); + } } return NULL; diff --git a/res/res_calendar.c b/res/res_calendar.c index 2de995933..183b40845 100644 --- a/res/res_calendar.c +++ b/res/res_calendar.c @@ -65,6 +65,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <enum name="description"><para>The text description of the event</para></enum> <enum name="organizer"><para>The organizer of the event</para></enum> <enum name="location"><para>The location of the eventt</para></enum> + <enum name="categories"><para>The categories of the event</para></enum> + <enum name="priority"><para>The priority of the event</para></enum> <enum name="calendar"><para>The name of the calendar associated with the event</para></enum> <enum name="uid"><para>The unique identifier for this event</para></enum> <enum name="start"><para>The start time of the event</para></enum> @@ -112,6 +114,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <enum name="description"><para>The full event description</para></enum> <enum name="organizer"><para>The event organizer</para></enum> <enum name="location"><para>The event location</para></enum> + <enum name="categories"><para>The categories of the event</para></enum> + <enum name="priority"><para>The priority of the event</para></enum> <enum name="calendar"><para>The name of the calendar associted with the event</para></enum> <enum name="uid"><para>The unique identifier for the event</para></enum> <enum name="start"><para>The start time of the event (in seconds since epoch)</para></enum> @@ -142,6 +146,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <enum name="description"><para>The full event description</para></enum> <enum name="organizer"><para>The event organizer</para></enum> <enum name="location"><para>The event location</para></enum> + <enum name="categories"><para>The categories of the event</para></enum> + <enum name="priority"><para>The priority of the event</para></enum> <enum name="uid"><para>The unique identifier for the event</para></enum> <enum name="start"><para>The start time of the event (in seconds since epoch)</para></enum> <enum name="end"><para>The end time of the event (in seconds since epoch)</para></enum> @@ -786,6 +792,8 @@ static void copy_event_data(struct ast_calendar_event *dst, struct ast_calendar_ ast_string_field_set(dst, organizer, src->organizer); ast_string_field_set(dst, location, src->location); ast_string_field_set(dst, uid, src->uid); + ast_string_field_set(dst, categories, src->categories); + dst->priority = src->priority; dst->owner = src->owner; dst->start = src->start; dst->end = src->end; @@ -1228,6 +1236,10 @@ static int calendar_query_result_exec(struct ast_channel *chan, const char *cmd, ast_copy_string(buf, entry->event->organizer, len); } else if (!strcasecmp(args.field, "location")) { ast_copy_string(buf, entry->event->location, len); + } else if (!strcasecmp(args.field, "categories")) { + ast_copy_string(buf, entry->event->categories, len); + } else if (!strcasecmp(args.field, "priority")) { + snprintf(buf, len, "%d", entry->event->priority); } else if (!strcasecmp(args.field, "calendar")) { ast_copy_string(buf, entry->event->owner->name, len); } else if (!strcasecmp(args.field, "uid")) { @@ -1313,6 +1325,10 @@ static int calendar_write_exec(struct ast_channel *chan, const char *cmd, char * ast_string_field_set(event, organizer, values.value[j]); } else if (!strcasecmp(fields.field[i], "location")) { ast_string_field_set(event, location, values.value[j]); + } else if (!strcasecmp(fields.field[i], "categories")) { + ast_string_field_set(event, categories, values.value[j]); + } else if (!strcasecmp(fields.field[i], "priority")) { + event->priority = atoi(values.value[j]); } else if (!strcasecmp(fields.field[i], "uid")) { ast_string_field_set(event, uid, values.value[j]); } else if (!strcasecmp(fields.field[i], "start")) { @@ -1468,6 +1484,8 @@ static char *handle_show_calendar(struct ast_cli_entry *e, int cmd, struct ast_c ast_cli(a->fd, FORMAT2, "Description", event->description); ast_cli(a->fd, FORMAT2, "Organizer", event->organizer); ast_cli(a->fd, FORMAT2, "Location", event->location); + ast_cli(a->fd, FORMAT2, "Cartegories", event->categories); + ast_cli(a->fd, "%-12.12s: %d\n", "Priority", event->priority); ast_cli(a->fd, FORMAT2, "UID", event->uid); ast_cli(a->fd, FORMAT2, "Start", epoch_to_string(buf, sizeof(buf), event->start)); ast_cli(a->fd, FORMAT2, "End", epoch_to_string(buf, sizeof(buf), event->end)); @@ -1539,6 +1557,10 @@ static int calendar_event_read(struct ast_channel *chan, const char *cmd, char * ast_copy_string(buf, event->organizer, len); } else if (!strcasecmp(data, "location")) { ast_copy_string(buf, event->location, len); + } else if (!strcasecmp(data, "categories")) { + ast_copy_string(buf, event->categories, len); + } else if (!strcasecmp(data, "priority")) { + snprintf(buf, len, "%d", event->priority); } else if (!strcasecmp(data, "calendar")) { ast_copy_string(buf, event->owner->name, len); } else if (!strcasecmp(data, "uid")) { diff --git a/res/res_calendar_caldav.c b/res/res_calendar_caldav.c index 664f25b87..e0d8c483e 100644 --- a/res/res_calendar_caldav.c +++ b/res/res_calendar_caldav.c @@ -216,6 +216,12 @@ static int caldav_write_event(struct ast_calendar_event *event) if (!ast_strlen_zero(event->location)) { icalcomponent_add_property(icalevent, icalproperty_new_location(event->location)); } + if (!ast_strlen_zero(event->categories)) { + icalcomponent_add_property(icalevent, icalproperty_new_categories(event->categories)); + } + if (event->priority > 0) { + icalcomponent_add_property(icalevent, icalproperty_new_priority(event->priority)); + } switch (event->busy_state) { case AST_CALENDAR_BS_BUSY: @@ -365,6 +371,14 @@ static void caldav_add_event(icalcomponent *comp, struct icaltime_span *span, vo ast_string_field_set(event, location, icalproperty_get_value_as_string(prop)); } + if ((prop = icalcomponent_get_first_property(comp, ICAL_CATEGORIES_PROPERTY))) { + ast_string_field_set(event, categories, icalproperty_get_value_as_string(prop)); + } + + if ((prop = icalcomponent_get_first_property(comp, ICAL_PRIORITY_PROPERTY))) { + event->priority = icalvalue_get_integer(icalproperty_get_value(prop)); + } + if ((prop = icalcomponent_get_first_property(comp, ICAL_UID_PROPERTY))) { ast_string_field_set(event, uid, icalproperty_get_value_as_string(prop)); } else { diff --git a/res/res_calendar_ews.c b/res/res_calendar_ews.c index 931e36854..840ca104e 100644 --- a/res/res_calendar_ews.c +++ b/res/res_calendar_ews.c @@ -87,6 +87,9 @@ enum { XML_EVENT_ATTENDEE, XML_EVENT_MAILBOX, XML_EVENT_EMAIL_ADDRESS, + XML_EVENT_CATEGORIES, + XML_EVENT_CATEGORY, + XML_EVENT_IMPORTANCE, }; struct ewscal_pvt { @@ -271,6 +274,23 @@ static int startelm(void *userdata, int parent, const char *nspace, const char * } ast_str_reset(ctx->cdata); return XML_EVENT_LOCATION; + } else if (!strcmp(name, "Categories")) { + /* Event categories */ + if (!ctx->cdata) { + return NE_XML_ABORT; + } + ast_str_reset(ctx->cdata); + return XML_EVENT_CATEGORIES; + } else if (parent == XML_EVENT_CATEGORIES && !strcmp(name, "String")) { + /* Event category */ + return XML_EVENT_CATEGORY; + } else if (!strcmp(name, "Importance")) { + /* Event importance (priority) */ + if (!ctx->cdata) { + return NE_XML_ABORT; + } + ast_str_reset(ctx->cdata); + return XML_EVENT_IMPORTANCE; } else if (!strcmp(name, "RequiredAttendees") || !strcmp(name, "OptionalAttendees")) { return XML_EVENT_ATTENDEE_LIST; } else if (!strcmp(name, "Attendee") && parent == XML_EVENT_ATTENDEE_LIST) { @@ -331,6 +351,13 @@ static int cdata(void *userdata, int state, const char *cdata, size_t len) ctx->event->busy_state = AST_CALENDAR_BS_FREE; } break; + case XML_EVENT_CATEGORY: + if (ast_str_strlen(ctx->cdata) == 0) { + ast_str_set(&ctx->cdata, 0, "%s", data); + } else { + ast_str_append(&ctx->cdata, 0, ",%s", data); + } + break; default: ast_str_append(&ctx->cdata, 0, "%s", data); } @@ -364,6 +391,22 @@ static int endelm(void *userdata, int state, const char *nspace, const char *nam ast_string_field_set(ctx->event, location, ast_str_buffer(ctx->cdata)); ast_debug(3, "EWS: XML: Location: %s\n", ctx->event->location); ast_str_reset(ctx->cdata); + } else if (!strcmp(name, "Categories")) { + /* Event categories end */ + ast_string_field_set(ctx->event, categories, ast_str_buffer(ctx->cdata)); + ast_debug(3, "EWS: XML: Categories: %s\n", ctx->event->categories); + ast_str_reset(ctx->cdata); + } else if (!strcmp(name, "Importance")) { + /* Event importance end */ + if (!strcmp(ast_str_buffer(ctx->cdata), "Low")) { + ctx->event->priority = 9; + } else if (!strcmp(ast_str_buffer(ctx->cdata), "Normal")) { + ctx->event->priority = 5; + } else if (!strcmp(ast_str_buffer(ctx->cdata), "High")) { + ctx->event->priority = 1; + } + ast_debug(3, "EWS: XML: Importance: %s (%d)\n", ast_str_buffer(ctx->cdata), ctx->event->priority); + ast_str_reset(ctx->cdata); } else if (state == XML_EVENT_EMAIL_ADDRESS) { struct ast_calendar_attendee *attendee; @@ -501,6 +544,7 @@ static int ewscal_write_event(struct ast_calendar_event *event) .pvt = pvt, }; int ret; + char *category, *categories; if (!pvt) { return -1; @@ -531,12 +575,7 @@ static int ewscal_write_event(struct ast_calendar_event *event) "<End>%s</End>" "<IsAllDayEvent>false</IsAllDayEvent>" "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>" - "<Location>%s</Location>" - "</t:CalendarItem>" - "</Items>" - "</CreateItem>" - "</soap:Body>" - "</soap:Envelope>", + "<Location>%s</Location>", event->summary, event->description, mstime(event->start, start, sizeof(start)), @@ -544,6 +583,37 @@ static int ewscal_write_event(struct ast_calendar_event *event) msstatus(event->busy_state), event->location ); + /* Event priority */ + switch (event->priority) { + case 1: + case 2: + case 3: + case 4: + ast_str_append(&request, 0, "<Importance>High</Importance>"); + break; + case 5: + ast_str_append(&request, 0, "<Importance>Normal</Importance>"); + break; + case 6: + case 7: + case 8: + case 9: + ast_str_append(&request, 0, "<Importance>Low</Importance>"); + break; + } + /* Event categories*/ + if (strlen(event->categories) > 0) { + ast_str_append(&request, 0, "<Categories>"); + categories = ast_strdupa(event->categories); /* Duplicate string, since strsep() is destructive */ + category = strsep(&categories, ","); + while (category != NULL) { + ast_str_append(&request, 0, "<String>%s</String>", category); + category = strsep(&categories, ","); + } + ast_str_append(&request, 0, "</Categories>"); + } + /* Finish request */ + ast_str_append(&request, 0, "</t:CalendarItem></Items></CreateItem></soap:Body></soap:Envelope>"); ret = send_ews_request_and_parse(request, &ctx); diff --git a/res/res_calendar_icalendar.c b/res/res_calendar_icalendar.c index 2eda35245..300da2ac9 100644 --- a/res/res_calendar_icalendar.c +++ b/res/res_calendar_icalendar.c @@ -224,6 +224,14 @@ static void icalendar_add_event(icalcomponent *comp, struct icaltime_span *span, ast_string_field_set(event, location, icalproperty_get_value_as_string(prop)); } + if ((prop = icalcomponent_get_first_property(comp, ICAL_CATEGORIES_PROPERTY))) { + ast_string_field_set(event, categories, icalproperty_get_value_as_string(prop)); + } + + if ((prop = icalcomponent_get_first_property(comp, ICAL_PRIORITY_PROPERTY))) { + event->priority = icalvalue_get_integer(icalproperty_get_value(prop)); + } + if ((prop = icalcomponent_get_first_property(comp, ICAL_UID_PROPERTY))) { ast_string_field_set(event, uid, icalproperty_get_value_as_string(prop)); } else { diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c index 15534c7fa..64628c286 100644 --- a/res/res_config_odbc.c +++ b/res/res_config_odbc.c @@ -62,7 +62,7 @@ static void decode_chunk(char *chunk) { for (; *chunk; chunk++) { if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) { - sscanf(chunk + 1, "%02hhd", chunk); + sscanf(chunk + 1, "%02hhX", chunk); memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1); } } diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c index e356e8f83..b4398e8c9 100644 --- a/res/res_config_pgsql.c +++ b/res/res_config_pgsql.c @@ -1179,12 +1179,16 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap size, column->type); res = -1; } - } else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) { - ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); - res = -1; - } else if (strncmp(column->type, "timestamp", 9) == 0 && type != RQ_DATETIME) { - ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); - res = -1; + } else if (strncmp(column->type, "float", 5) == 0) { + if (!ast_rq_is_int(type) && type != RQ_FLOAT) { + ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); + res = -1; + } + } else if (strncmp(column->type, "timestamp", 9) == 0) { + if (type != RQ_DATETIME && type != RQ_DATE) { + ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); + res = -1; + } } else { /* There are other types that no module implements yet */ ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name); res = -1; diff --git a/res/res_fax.c b/res/res_fax.c index 03fd128a7..3da2d02f8 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -387,9 +387,6 @@ static struct ast_fax_session_details *session_details_new(void) d->modems = general_options.modems; d->minrate = general_options.minrate; d->maxrate = general_options.maxrate; - ast_string_field_set(d, result, "FAILED"); - ast_string_field_set(d, resultstr, "error starting fax session"); - ast_string_field_set(d, error, "INIT_ERROR"); return d; } @@ -500,6 +497,45 @@ static int ast_fax_modem_to_str(enum ast_fax_modems bits, char *tbuf, size_t buf return 0; } +static int check_modem_rate(enum ast_fax_modems modems, unsigned int rate) +{ + switch (rate) { + case 2400: + if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) { + return 1; + } + break; + case 4800: + if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) { + return 1; + } + break; + case 7200: + case 9600: + if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) { + return 1; + } + break; + case 12000: + case 14400: + if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V34))) { + return 1; + } + break; + case 28800: + case 33600: + if (!(modems & AST_FAX_MODEM_V34)) { + return 1; + } + break; + default: + /* this should never happen */ + return 1; + } + + return 0; +} + /*! \brief register a FAX technology module */ int ast_fax_tech_register(struct ast_fax_tech *tech) { @@ -1363,7 +1399,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_ /*! \brief initiate a receive FAX session */ static int receivefax_exec(struct ast_channel *chan, const char *data) { - char *parse; + char *parse, modems[128] = ""; int channel_alive; struct ast_fax_session_details *details; struct ast_fax_document *doc; @@ -1390,8 +1426,40 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) return -1; } + ast_string_field_set(details, result, "FAILED"); + ast_string_field_set(details, resultstr, "error starting fax session"); + ast_string_field_set(details, error, "INIT_ERROR"); set_channel_variables(chan, details); + if (details->maxrate < details->minrate) { + ast_string_field_set(details, error, "INVALID_ARGUMENTS"); + ast_string_field_set(details, resultstr, "maxrate is less than minrate"); + set_channel_variables(chan, details); + ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate); + ao2_ref(details, -1); + return -1; + } + + if (check_modem_rate(details->modems, details->minrate)) { + ast_fax_modem_to_str(details->modems, modems, sizeof(modems)); + ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate); + ast_string_field_set(details, error, "INVALID_ARGUMENTS"); + ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings"); + set_channel_variables(chan, details); + ao2_ref(details, -1); + return -1; + } + + if (check_modem_rate(details->modems, details->maxrate)) { + ast_fax_modem_to_str(details->modems, modems, sizeof(modems)); + ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate); + ast_string_field_set(details, error, "INVALID_ARGUMENTS"); + ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings"); + set_channel_variables(chan, details); + ao2_ref(details, -1); + return -1; + } + if (ast_strlen_zero(data)) { ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "invalid arguments"); @@ -1768,7 +1836,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det /*! \brief initiate a send FAX session */ static int sendfax_exec(struct ast_channel *chan, const char *data) { - char *parse, *filenames, *c; + char *parse, *filenames, *c, modems[128] = ""; int channel_alive, file_count; struct ast_fax_session_details *details; struct ast_fax_document *doc; @@ -1795,8 +1863,40 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) return -1; } + ast_string_field_set(details, result, "FAILED"); + ast_string_field_set(details, resultstr, "error starting fax session"); + ast_string_field_set(details, error, "INIT_ERROR"); set_channel_variables(chan, details); + if (details->maxrate < details->minrate) { + ast_string_field_set(details, error, "INVALID_ARGUMENTS"); + ast_string_field_set(details, resultstr, "maxrate is less than minrate"); + set_channel_variables(chan, details); + ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate); + ao2_ref(details, -1); + return -1; + } + + if (check_modem_rate(details->modems, details->minrate)) { + ast_fax_modem_to_str(details->modems, modems, sizeof(modems)); + ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate); + ast_string_field_set(details, error, "INVALID_ARGUMENTS"); + ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings"); + set_channel_variables(chan, details); + ao2_ref(details, -1); + return -1; + } + + if (check_modem_rate(details->modems, details->maxrate)) { + ast_fax_modem_to_str(details->modems, modems, sizeof(modems)); + ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate); + ast_string_field_set(details, error, "INVALID_ARGUMENTS"); + ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings"); + set_channel_variables(chan, details); + ao2_ref(details, -1); + return -1; + } + if (ast_strlen_zero(data)) { ast_string_field_set(details, error, "INVALID_ARGUMENTS"); ast_string_field_set(details, resultstr, "invalid arguments"); @@ -2292,6 +2392,7 @@ static int set_config(const char *config_file) struct ast_config *cfg; struct ast_variable *v; struct ast_flags config_flags = { 0 }; + char modems[128] = ""; /* set defaults */ general_options.minrate = RES_FAX_MINRATE; @@ -2342,6 +2443,23 @@ static int set_config(const char *config_file) ast_config_destroy(cfg); + if (general_options.maxrate < general_options.minrate) { + ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", general_options.maxrate, general_options.minrate); + return -1; + } + + if (check_modem_rate(general_options.modems, general_options.minrate)) { + ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems)); + ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, general_options.minrate); + return -1; + } + + if (check_modem_rate(general_options.modems, general_options.maxrate)) { + ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems)); + ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, general_options.maxrate); + return -1; + } + return 0; } diff --git a/res/res_jabber.c b/res/res_jabber.c index 5238441f1..13c7bf2cd 100644 --- a/res/res_jabber.c +++ b/res/res_jabber.c @@ -1283,38 +1283,27 @@ static int aji_tls_handshake(struct aji_client *client) */ static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout) { - int sock; - fd_set fds; - struct timeval tv, *tvptr = NULL; + struct pollfd pfd = { .events = POLLIN }; int len, res; #ifdef HAVE_OPENSSL if (aji_is_secure(client)) { - sock = SSL_get_fd(client->ssl_session); - if (sock < 0) { + pfd.fd = SSL_get_fd(client->ssl_session); + if (pfd.fd < 0) { return -1; } } else #endif /* HAVE_OPENSSL */ - sock = iks_fd(client->p); - - memset(&tv, 0, sizeof(struct timeval)); - FD_ZERO(&fds); - FD_SET(sock, &fds); - tv.tv_sec = timeout; - - /* NULL value for tvptr makes ast_select wait indefinitely */ - tvptr = (timeout != -1) ? &tv : NULL; + pfd.fd = iks_fd(client->p); - /* ast_select emulates linux behaviour in terms of timeout handling */ - res = ast_select(sock + 1, &fds, NULL, NULL, tvptr); + res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1); if (res > 0) { #ifdef HAVE_OPENSSL if (aji_is_secure(client)) { len = SSL_read(client->ssl_session, buffer, buf_len); } else #endif /* HAVE_OPENSSL */ - len = recv(sock, buffer, buf_len, 0); + len = recv(pfd.fd, buffer, buf_len, 0); if (len > 0) { return len; diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index 2361b2280..6024e9da8 100644 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -41,16 +41,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <netinet/in.h> #include <sys/stat.h> #include <dirent.h> -#include <sys/ioctl.h> #ifdef SOLARIS #include <thread.h> #endif -#ifdef HAVE_DAHDI -#include <dahdi/user.h> -#endif - #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/channel.h" @@ -68,6 +63,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/manager.h" #include "asterisk/paths.h" #include "asterisk/astobj2.h" +#include "asterisk/timing.h" +#include "asterisk/time.h" +#include "asterisk/poll-compat.h" #define INITIAL_NUM_FILES 8 #define HANDLE_REF 1 @@ -198,10 +196,10 @@ struct mohclass { pthread_t thread; /*! Source of audio */ int srcfd; - /*! FD for timing source */ - int pseudofd; + /*! Generic timer */ + struct ast_timer *timer; /*! Created on the fly, from RT engine */ - int realtime; + unsigned int realtime:1; unsigned int delete:1; AST_LIST_HEAD_NOLOCK(, mohdata) members; AST_LIST_ENTRY(mohclass) list; @@ -320,10 +318,17 @@ static int ast_moh_files_next(struct ast_channel *chan) state->samples = 0; } - if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) { + for (tries = 0; tries < state->class->total_files; ++tries) { + if (ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) { + break; + } + ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno)); state->pos++; state->pos %= state->class->total_files; + } + + if (tries == state->class->total_files) { return -1; } @@ -607,9 +612,8 @@ static void *monmp3thread(void *data) struct mohclass *class = data; struct mohdata *moh; - char buf[8192]; short sbuf[8192]; - int res, res2; + int res = 0, res2; int len; struct timeval deadline, tv_tmp; @@ -626,12 +630,22 @@ static void *monmp3thread(void *data) pthread_testcancel(); } } - if (class->pseudofd > -1) { + if (class->timer) { + struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN, }; + struct timeval tv; + #ifdef SOLARIS thr_yield(); #endif /* Pause some amount of time */ - res = read(class->pseudofd, buf, sizeof(buf)); + tv = ast_tvnow(); + if (ast_poll(&pfd, 1, -1) > 0) { + ast_timer_ack(class->timer, 1); + res = 320; + } else { + ast_log(LOG_ERROR, "poll() failed: %s\n", strerror(errno)); + res = 0; + } pthread_testcancel(); } else { long delta; @@ -1107,6 +1121,19 @@ static int init_files_class(struct mohclass *class) return 0; } +static void moh_rescan_files(void) { + struct ao2_iterator i; + struct mohclass *c; + + i = ao2_iterator_init(mohclasses, 0); + + while ((c = ao2_iterator_next(&i))) { + moh_scan_files(c); + ao2_ref(c, -1); + } + + ao2_iterator_destroy(&i); +} static int moh_diff(struct mohclass *old, struct mohclass *new) { @@ -1129,10 +1156,6 @@ static int moh_diff(struct mohclass *old, struct mohclass *new) static int init_app_class(struct mohclass *class) { -#ifdef HAVE_DAHDI - int x; -#endif - if (!strcasecmp(class->mode, "custom")) { ast_set_flag(class, MOH_CUSTOM); } else if (!strcasecmp(class->mode, "mp3nb")) { @@ -1142,27 +1165,23 @@ static int init_app_class(struct mohclass *class) } else if (!strcasecmp(class->mode, "quietmp3")) { ast_set_flag(class, MOH_QUIET); } - + class->srcfd = -1; - class->pseudofd = -1; - -#ifdef HAVE_DAHDI - /* Open /dev/zap/pseudo for timing... Is - there a better, yet reliable way to do this? */ - class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY); - if (class->pseudofd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n"); - } else { - x = 320; - ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x); + + if (!(class->timer = ast_timer_open())) { + ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); + } + if (class->timer && ast_timer_set_rate(class->timer, 25)) { + ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); + ast_timer_close(class->timer); + class->timer = NULL; } -#endif if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) { ast_log(LOG_WARNING, "Unable to create moh thread...\n"); - if (class->pseudofd > -1) { - close(class->pseudofd); - class->pseudofd = -1; + if (class->timer) { + ast_timer_close(class->timer); + class->timer = NULL; } return -1; } @@ -1192,7 +1211,7 @@ static int _moh_register(struct mohclass *moh, int reload, int unref, const char time(&moh->start); moh->start -= respawn_time; - + if (!strcasecmp(moh->mode, "files")) { if (init_files_class(moh)) { if (unref) { @@ -1222,7 +1241,7 @@ static int _moh_register(struct mohclass *moh, int reload, int unref, const char if (unref) { moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container"); } - + return 0; } @@ -1390,7 +1409,7 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con time(&mohclass->start); mohclass->start -= respawn_time; - + if (!strcasecmp(mohclass->mode, "files")) { if (!moh_scan_files(mohclass)) { mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)"); @@ -1408,21 +1427,17 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET); else if (!strcasecmp(mohclass->mode, "quietmp3")) ast_set_flag(mohclass, MOH_QUIET); - + mohclass->srcfd = -1; -#ifdef HAVE_DAHDI - /* Open /dev/dahdi/pseudo for timing... Is - there a better, yet reliable way to do this? */ - mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY); - if (mohclass->pseudofd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n"); - } else { - int x = 320; - ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x); + if (!(mohclass->timer = ast_timer_open())) { + ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno)); } -#else - mohclass->pseudofd = -1; -#endif + if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) { + ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno)); + ast_timer_close(mohclass->timer); + mohclass->timer = NULL; + } + /* Let's check if this channel already had a moh class before */ if (state && state->class) { /* Class already exist for this channel */ @@ -1435,9 +1450,9 @@ static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, con } else { if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) { ast_log(LOG_WARNING, "Unable to create moh...\n"); - if (mohclass->pseudofd > -1) { - close(mohclass->pseudofd); - mohclass->pseudofd = -1; + if (mohclass->timer) { + ast_timer_close(mohclass->timer); + mohclass->timer = NULL; } mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)"); return -1; @@ -1612,13 +1627,16 @@ static int load_moh_classes(int reload) ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes"); } + if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + moh_rescan_files(); + } return 0; } if (reload) { ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes"); } - + ast_clear_flag(global_flags, AST_FLAGS_ALL); cat = ast_category_browse(cfg, NULL); diff --git a/res/res_odbc.c b/res/res_odbc.c index d2ee97948..6e179efff 100644 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -780,6 +780,7 @@ static int load_odbc_config(void) pooling = 0; limit = 0; bse = 1; + conntimeout = 10; forcecommit = 0; isolation = SQL_TXN_READ_COMMITTED; for (v = ast_variable_browse(config, cat); v; v = v->next) { @@ -1203,7 +1204,7 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags unsigned char state[10], diagnostic[256]; if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) { - ast_debug(1, "Class not found!\n"); + ast_debug(1, "Class '%s' not found!\n", name); return NULL; } diff --git a/res/res_pktccops.c b/res/res_pktccops.c index 8069e723d..33ecc3817 100644 --- a/res/res_pktccops.c +++ b/res/res_pktccops.c @@ -703,9 +703,8 @@ static void *do_pktccops(void *data) int res, nfds, len; struct copsmsg *recmsg, *sendmsg; struct copsmsg recmsgb, sendmsgb; - fd_set rfds; - struct timeval tv; - struct pktcobj *pobject; + struct pollfd *pfds = NULL, *tmp; + struct pktcobj *pobject; struct cops_cmts *cmts; struct cops_gate *gate; char *sobjp; @@ -719,9 +718,8 @@ static void *do_pktccops(void *data) ast_debug(3, "COPS: thread started\n"); for (;;) { - tv.tv_sec = 1; - tv.tv_usec = 0; - FD_ZERO(&rfds); + ast_free(pfds); + pfds = NULL; nfds = 0; AST_LIST_LOCK(&cmts_list); AST_LIST_TRAVERSE(&cmts_list, cmts, list) { @@ -735,15 +733,27 @@ static void *do_pktccops(void *data) } } if (cmts->sfd > 0) { - FD_SET(cmts->sfd, &rfds); - if (cmts->sfd > nfds) nfds = cmts->sfd; + if (!(tmp = ast_realloc(pfds, (nfds + 1) * sizeof(*pfds)))) { + continue; + } + pfds = tmp; + pfds[nfds].fd = cmts->sfd; + pfds[nfds].events = POLLIN; + pfds[nfds].revents = 0; + nfds++; } else { cmts->sfd = cops_connect(cmts->host, cmts->port); if (cmts->sfd > 0) { cmts->state = 1; if (cmts->sfd > 0) { - FD_SET(cmts->sfd, &rfds); - if (cmts->sfd > nfds) nfds = cmts->sfd; + if (!(tmp = ast_realloc(pfds, (nfds + 1) * sizeof(*pfds)))) { + continue; + } + pfds = tmp; + pfds[nfds].fd = cmts->sfd; + pfds[nfds].events = POLLIN; + pfds[nfds].revents = 0; + nfds++; } } } @@ -781,10 +791,11 @@ static void *do_pktccops(void *data) if (pktcreload == 2) { pktcreload = 0; } - if ((res = select(nfds + 1, &rfds, NULL, NULL, &tv))) { + if ((res = ast_poll(pfds, nfds, 1000))) { AST_LIST_LOCK(&cmts_list); AST_LIST_TRAVERSE(&cmts_list, cmts, list) { - if (FD_ISSET(cmts->sfd, &rfds)) { + int idx; + if ((idx = ast_poll_fd_index(pfds, nfds, cmts->sfd)) > -1 && (pfds[idx].revents & POLLIN)) { len = cops_getmsg(cmts->sfd, recmsg); if (len > 0) { ast_debug(3, "COPS: got from %s:\n Header: versflag=0x%.2x opcode=%i clienttype=0x%.4x msglength=%i\n", diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 396aed533..6ddd33902 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -270,6 +270,7 @@ static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_in static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_instance *instance0, struct ast_channel *chan1, struct ast_rtp_instance *instance1); static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username); static void ast_rtp_stop(struct ast_rtp_instance *instance); +static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc); /* RTP Engine Declaration */ static struct ast_rtp_engine asterisk_rtp_engine = { @@ -293,6 +294,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = { .dtmf_compatible = ast_rtp_dtmf_compatible, .stun_request = ast_rtp_stun_request, .stop = ast_rtp_stop, + .qos = ast_rtp_qos_set, }; static inline int rtp_debug_test_addr(struct ast_sockaddr *addr) @@ -715,12 +717,24 @@ static void ast_rtp_update_source(struct ast_rtp_instance *instance) static void ast_rtp_change_source(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance); unsigned int ssrc = ast_random(); + if (!rtp->lastts) { + ast_debug(3, "Not changing SSRC since we haven't sent any RTP yet\n"); + return; + } + /* We simply set this bit so that the next packet sent will have the marker bit turned on */ ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); ast_debug(3, "Changing ssrc from %u to %u due to a source change\n", rtp->ssrc, ssrc); + + if (srtp) { + ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc); + res_srtp->change_source(srtp, rtp->ssrc, ssrc); + } + rtp->ssrc = ssrc; return; @@ -2549,6 +2563,13 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance) ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); } +static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char *desc) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + return ast_set_qos(rtp->s, tos, cos, desc); +} + static char *rtp_do_debug_ip(struct ast_cli_args *a) { char *arg = ast_strdupa(a->argv[4]); diff --git a/res/res_srtp.c b/res/res_srtp.c index 546f0733d..f92154f90 100644 --- a/res/res_srtp.c +++ b/res/res_srtp.c @@ -53,14 +53,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/options.h" #include "asterisk/rtp_engine.h" +#include "asterisk/astobj2.h" struct ast_srtp { struct ast_rtp_instance *rtp; + struct ao2_container *policies; srtp_t session; const struct ast_srtp_cb *cb; void *data; unsigned char buf[8192 + AST_FRIENDLY_OFFSET]; - unsigned int has_stream:1; }; struct ast_srtp_policy { @@ -73,6 +74,7 @@ static int g_initialized = 0; static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, struct ast_srtp_policy *policy); static void ast_srtp_destroy(struct ast_srtp *srtp); static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy); +static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc, unsigned int to_ssrc); static int ast_srtp_unprotect(struct ast_srtp *srtp, void *buf, int *len, int rtcp); static int ast_srtp_protect(struct ast_srtp *srtp, void **buf, int *len, int rtcp); @@ -90,6 +92,7 @@ static struct ast_srtp_res srtp_res = { .create = ast_srtp_create, .destroy = ast_srtp_destroy, .add_stream = ast_srtp_add_stream, + .change_source = ast_srtp_change_source, .set_cb = ast_srtp_set_cb, .unprotect = ast_srtp_unprotect, .protect = ast_srtp_protect, @@ -144,6 +147,32 @@ static const char *srtp_errstr(int err) } } +static int policy_hash_fn(const void *obj, const int flags) +{ + const struct ast_srtp_policy *policy = obj; + + return policy->sp.ssrc.type == ssrc_specific ? policy->sp.ssrc.value : policy->sp.ssrc.type; +} + +static int policy_cmp_fn(void *obj, void *arg, int flags) +{ + const struct ast_srtp_policy *one = obj, *two = arg; + + return one->sp.ssrc.type == two->sp.ssrc.type && one->sp.ssrc.value == two->sp.ssrc.value; +} + +static struct ast_srtp_policy *find_policy(struct ast_srtp *srtp, const srtp_policy_t *policy, int flags) +{ + struct ast_srtp_policy tmp = { + .sp = { + .ssrc.type = policy->ssrc.type, + .ssrc.value = policy->ssrc.value, + }, + }; + + return ao2_t_find(srtp->policies, &tmp, flags, "Looking for policy"); +} + static struct ast_srtp *res_srtp_new(void) { struct ast_srtp *srtp; @@ -153,6 +182,11 @@ static struct ast_srtp *res_srtp_new(void) return NULL; } + if (!(srtp->policies = ao2_t_container_alloc(5, policy_hash_fn, policy_cmp_fn, "SRTP policy container"))) { + ast_free(srtp); + return NULL; + } + return srtp; } @@ -188,11 +222,21 @@ static void ast_srtp_policy_set_ssrc(struct ast_srtp_policy *policy, } } +static void policy_destructor(void *obj) +{ + struct ast_srtp_policy *policy = obj; + + if (policy->sp.key) { + ast_free(policy->sp.key); + policy->sp.key = NULL; + } +} + static struct ast_srtp_policy *ast_srtp_policy_alloc() { struct ast_srtp_policy *tmp; - if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { + if (!(tmp = ao2_t_alloc(sizeof(*tmp), policy_destructor, "Allocating policy"))) { ast_log(LOG_ERROR, "Unable to allocate memory for srtp_policy\n"); } @@ -201,11 +245,7 @@ static struct ast_srtp_policy *ast_srtp_policy_alloc() static void ast_srtp_policy_destroy(struct ast_srtp_policy *policy) { - if (policy->sp.key) { - ast_free(policy->sp.key); - policy->sp.key = NULL; - } - ast_free(policy); + ao2_t_ref(policy, -1, "Destroying policy"); } static int policy_set_suite(crypto_policy_t *p, enum ast_srtp_suite suite) @@ -344,6 +384,8 @@ static int ast_srtp_create(struct ast_srtp **srtp, struct ast_rtp_instance *rtp, temp->rtp = rtp; *srtp = temp; + ao2_t_link((*srtp)->policies, policy, "Created initial policy"); + return 0; } @@ -353,16 +395,52 @@ static void ast_srtp_destroy(struct ast_srtp *srtp) srtp_dealloc(srtp->session); } + ao2_t_callback(srtp->policies, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Unallocate policy"); + ao2_t_ref(srtp->policies, -1, "Destroying container"); + ast_free(srtp); } static int ast_srtp_add_stream(struct ast_srtp *srtp, struct ast_srtp_policy *policy) { - if (!srtp->has_stream && srtp_add_stream(srtp->session, &policy->sp) != err_status_ok) { + struct ast_srtp_policy *match; + + if ((match = find_policy(srtp, &policy->sp, OBJ_POINTER))) { + ast_debug(3, "Policy already exists, not re-adding\n"); + ao2_t_ref(match, -1, "Unreffing already existing policy"); return -1; } - srtp->has_stream = 1; + if (srtp_add_stream(srtp->session, &policy->sp) != err_status_ok) { + return -1; + } + + ao2_t_link(srtp->policies, policy, "Added additional stream"); + + return 0; +} + +static int ast_srtp_change_source(struct ast_srtp *srtp, unsigned int from_ssrc, unsigned int to_ssrc) +{ + struct ast_srtp_policy *match; + struct srtp_policy_t sp = { + .ssrc.type = ssrc_specific, + .ssrc.value = from_ssrc, + }; + err_status_t status; + + /* If we find a mach, return and unlink it from the container so we + * can change the SSRC (which is part of the hash) and then have + * ast_srtp_add_stream link it back in if all is well */ + if ((match = find_policy(srtp, &sp, OBJ_POINTER | OBJ_UNLINK))) { + match->sp.ssrc.value = to_ssrc; + if (ast_srtp_add_stream(srtp, match)) { + ast_log(LOG_WARNING, "Couldn't add stream\n"); + } else if ((status = srtp_remove_stream(srtp->session, from_ssrc))) { + ast_debug(3, "Couldn't remove stream (%d)\n", status); + } + ao2_t_ref(match, -1, "Unreffing found policy in change_source"); + } return 0; } diff --git a/res/res_stun_monitor.c b/res/res_stun_monitor.c new file mode 100644 index 000000000..54be1e44c --- /dev/null +++ b/res/res_stun_monitor.c @@ -0,0 +1,311 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * David Vossel <dvossel@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief STUN Network Monitor + * + * \author David Vossel <dvossel@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/event.h" +#include "asterisk/sched.h" +#include "asterisk/config.h" +#include "asterisk/stun.h" +#include "asterisk/netsock2.h" +#include "asterisk/lock.h" +#include <fcntl.h> + +static const int STANDARD_STUN_PORT = 3478; +static const int DEFAULT_MONITOR_REFRESH = 30; + +static const char stun_conf_file[] = "res_stun_monitor.conf"; +static struct ast_sched_thread *sched; + +static struct { + struct sockaddr_in stunaddr; /*!< The stun address we send requests to*/ + struct sockaddr_in externaladdr; /*!< current perceived external address. */ + ast_mutex_t lock; + unsigned int refresh; + int stunsock; + unsigned int monitor_enabled:1; + unsigned int externaladdr_known:1; +} args; + +static inline void stun_close_sock(void) +{ + if (args.stunsock != -1) { + close(args.stunsock); + args.stunsock = -1; + memset(&args.externaladdr, 0, sizeof(args.externaladdr)); + args.externaladdr_known = 0; + } +} + +/* \brief purge the stun socket's receive buffer before issuing a new request + * + * XXX Note that this is somewhat of a hack. This function is essentially doing + * a cleanup on the socket rec buffer to handle removing any STUN responses we have not + * handled. This is called before sending out a new STUN request so we don't read + * a latent previous response thinking it is new. + */ +static void stun_purge_socket(void) +{ + int flags = fcntl(args.stunsock, F_GETFL); + int res = 0; + unsigned char reply_buf[1024]; + + fcntl(args.stunsock, F_SETFL, flags | O_NONBLOCK); + while (res != -1) { + /* throw away everything in the buffer until we reach the end. */ + res = recv(args.stunsock, reply_buf, sizeof(reply_buf), 0); + } + fcntl(args.stunsock, F_SETFL, flags & ~O_NONBLOCK); +} + +/* \brief called by scheduler to send STUN request */ +static int stun_monitor_request(const void *blarg) +{ + int res; + int generate_event = 0; + struct sockaddr_in answer = { 0, }; + + + /* once the stun socket goes away, this scheduler item will go away as well */ + ast_mutex_lock(&args.lock); + if (args.stunsock == -1) { + ast_log(LOG_ERROR, "STUN monitor: can not send STUN request, socket is not open\n"); + goto monitor_request_cleanup; + } + + stun_purge_socket(); + + if (!(ast_stun_request(args.stunsock, &args.stunaddr, NULL, &answer)) && + (memcmp(&args.externaladdr, &answer, sizeof(args.externaladdr)))) { + const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr)); + int newport = ntohs(answer.sin_port); + + ast_log(LOG_NOTICE, "STUN MONITOR: Old external address/port %s:%d now seen as %s:%d \n", + ast_inet_ntoa(args.externaladdr.sin_addr), ntohs(args.externaladdr.sin_port), + newaddr, newport); + + memcpy(&args.externaladdr, &answer, sizeof(args.externaladdr)); + + if (args.externaladdr_known) { + /* the external address was already known, and has changed... generate event. */ + generate_event = 1; + + } else { + /* this was the first external address we found, do not alert listeners + * until this address changes to something else. */ + args.externaladdr_known = 1; + } + } + + if (generate_event) { + struct ast_event *event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END); + if (!event) { + ast_log(LOG_ERROR, "STUN monitor: could not create AST_EVENT_NETWORK_CHANGE event.\n"); + goto monitor_request_cleanup; + } + if (ast_event_queue(event)) { + ast_event_destroy(event); + event = NULL; + ast_log(LOG_ERROR, "STUN monitor: could not queue AST_EVENT_NETWORK_CHANGE event.\n"); + goto monitor_request_cleanup; + } + } + +monitor_request_cleanup: + /* always refresh this scheduler item. It will be removed elsewhere when + * it is supposed to go away */ + res = args.refresh * 1000; + ast_mutex_unlock(&args.lock); + + return res; +} + +/* \brief stops the stun monitor thread + * \note do not hold the args->lock while calling this + */ +static void stun_stop_monitor(void) +{ + if (sched) { + sched = ast_sched_thread_destroy(sched); + ast_log(LOG_NOTICE, "STUN monitor stopped\n"); + } + /* it is only safe to destroy the socket without holding arg->lock + * after the sched thread is destroyed */ + stun_close_sock(); +} + +/* \brief starts the stun monitor thread + * \note The args->lock MUST be held when calling this function + */ +static int stun_start_monitor(void) +{ + struct ast_sockaddr dst; + /* clean up any previous open socket */ + stun_close_sock(); + + /* create destination ast_sockaddr */ + ast_sockaddr_from_sin(&dst, &args.stunaddr); + + /* open new socket binding */ + args.stunsock = socket(AF_INET, SOCK_DGRAM, 0); + if (args.stunsock < 0) { + ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno)); + return -1; + } + + if (ast_connect(args.stunsock, &dst) != 0) { + ast_log(LOG_WARNING, "SIP STUN Failed to connect to %s\n", ast_sockaddr_stringify(&dst)); + stun_close_sock(); + return -1; + } + + /* if scheduler thread is not started, make sure to start it now */ + if (sched) { + return 0; /* already started */ + } + + if (!(sched = ast_sched_thread_create())) { + ast_log(LOG_ERROR, "Failed to create stun monitor scheduler thread\n"); + stun_close_sock(); + return -1; + } + + if (ast_sched_thread_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) { + ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n"); + sched = ast_sched_thread_destroy(sched); + stun_close_sock(); + return -1; + } + + ast_log(LOG_NOTICE, "STUN monitor started\n"); + return 0; +} + +static int load_config(int startup) +{ + struct ast_flags config_flags = { 0, }; + struct ast_config *cfg; + struct ast_variable *v; + + if (!startup) { + ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED); + } + + if (!(cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags)) || + cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Unable to load config %s\n", stun_conf_file); + return -1; + } + + if (cfg == CONFIG_STATUS_FILEUNCHANGED && !startup) { + return 0; + } + + /* set defaults */ + args.monitor_enabled = 0; + memset(&args.stunaddr, 0, sizeof(args.stunaddr)); + args.refresh = DEFAULT_MONITOR_REFRESH; + + for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { + if (!strcasecmp(v->name, "stunaddr")) { + args.stunaddr.sin_port = htons(STANDARD_STUN_PORT); + if (ast_parse_arg(v->value, PARSE_INADDR, &args.stunaddr)) { + ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value); + } else { + ast_log(LOG_NOTICE, "STUN monitor enabled: %s\n", v->value); + args.monitor_enabled = 1; + } + } else if (!strcasecmp(v->name, "stunrefresh")) { + if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) { + ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno); + args.refresh = DEFAULT_MONITOR_REFRESH; + } else { + ast_log(LOG_NOTICE, "STUN Monitor set to refresh every %d seconds\n", args.refresh); + } + } else { + ast_log(LOG_WARNING, "SIP STUN: invalid config option %s at line %d\n", v->value, v->lineno); + } + } + + ast_config_destroy(cfg); + + return 0; +} + +static int __reload(int startup) +{ + int res; + + ast_mutex_lock(&args.lock); + if (!(res = load_config(startup)) && args.monitor_enabled) { + res = stun_start_monitor(); + } + ast_mutex_unlock(&args.lock); + + if ((res == -1) || !args.monitor_enabled) { + args.monitor_enabled = 0; + stun_stop_monitor(); + } + + return res; +} + +static int reload(void) +{ + return __reload(0); +} + +static int unload_module(void) +{ + stun_stop_monitor(); + ast_mutex_destroy(&args.lock); + return 0; +} + +static int load_module(void) +{ + ast_mutex_init(&args.lock); + args.stunsock = -1; + memset(&args.externaladdr, 0, sizeof(args.externaladdr)); + args.externaladdr_known = 0; + sched = NULL; + if (__reload(1)) { + stun_stop_monitor(); + ast_mutex_destroy(&args.lock); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor", + .load = load_module, + .unload = unload_module, + .reload = reload, + .load_pri = AST_MODPRI_CHANNEL_DEPEND + ); |