struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_session_args *desc) { int x = 1; struct ast_tcptls_session_instance *tcptls_session = NULL; /* Do nothing if nothing has changed */ if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) { ast_debug(1, "Nothing changed in %s\n", desc->name); return NULL; } /* If we return early, there is no connection */ ast_sockaddr_setnull(&desc->old_address); if (desc->accept_fd != -1) close(desc->accept_fd); desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP); if (desc->accept_fd < 0) { ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno)); return NULL; } /* if a local address was specified, bind to it so the connection will originate from the desired address */ if (!ast_sockaddr_isnull(&desc->local_address)) { setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); if (ast_bind(desc->accept_fd, &desc->local_address)) { ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n", desc->name, ast_sockaddr_stringify(&desc->local_address), strerror(errno)); goto error; } } if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor))) goto error; ast_mutex_init(&tcptls_session->lock); tcptls_session->client = 1; tcptls_session->fd = desc->accept_fd; tcptls_session->parent = desc; tcptls_session->parent->worker_fn = NULL; ast_sockaddr_copy(&tcptls_session->remote_address, &desc->remote_address); /* Set current info */ ast_sockaddr_copy(&desc->old_address, &desc->remote_address); return tcptls_session; error: close(desc->accept_fd); desc->accept_fd = -1; if (tcptls_session) ao2_ref(tcptls_session, -1); return NULL; }
static int send_keepalive(const void *data) { struct ast_sip_session_media *session_media = (struct ast_sip_session_media *) data; struct ast_rtp_instance *rtp = session_media->rtp; int keepalive; time_t interval; int send_keepalive; keepalive = ast_rtp_instance_get_keepalive(rtp); if (!ast_sockaddr_isnull(&session_media->direct_media_addr)) { ast_debug(3, "Not sending RTP keepalive on RTP instance %p since direct media is in use\n", rtp); return keepalive * 1000; } interval = time(NULL) - ast_rtp_instance_get_last_tx(rtp); send_keepalive = interval >= keepalive; ast_debug(3, "It has been %d seconds since RTP was last sent on instance %p. %sending keepalive\n", (int) interval, rtp, send_keepalive ? "S" : "Not s"); if (send_keepalive) { ast_rtp_instance_sendcng(rtp, 0); return keepalive * 1000; } return (keepalive - interval) * 1000; }
/*! \brief See if we pass debug IP filter */ static inline int pjsip_log_test_addr(const char *address, int port) { struct ast_sockaddr test_addr; if (logging_mode == LOGGING_MODE_DISABLED) { return 0; } /* A null logging address means we'll debug any address */ if (ast_sockaddr_isnull(&log_addr)) { return 1; } /* A null address was passed in. Just reject it. */ if (ast_strlen_zero(address)) { return 0; } ast_sockaddr_parse(&test_addr, address, PARSE_PORT_IGNORE); ast_sockaddr_set_port(&test_addr, port); /* If no port was specified for a debug address, just compare the * addresses, otherwise compare the address and port */ if (ast_sockaddr_port(&log_addr)) { return !ast_sockaddr_cmp(&log_addr, &test_addr); } else { return !ast_sockaddr_cmp_addr(&log_addr, &test_addr); } }
/*! \brief Function which adds ICE attributes to a media stream */ static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media) { struct ast_rtp_engine_ice *ice; struct ao2_container *candidates; const char *username, *password; pj_str_t stmp; pjmedia_sdp_attr *attr; struct ao2_iterator it_candidates; struct ast_rtp_engine_ice_candidate *candidate; if (!session->endpoint->media.rtp.ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp)) || !(candidates = ice->get_local_candidates(session_media->rtp))) { return; } if ((username = ice->get_ufrag(session_media->rtp))) { attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", pj_cstr(&stmp, username)); media->attr[media->attr_count++] = attr; } if ((password = ice->get_password(session_media->rtp))) { attr = pjmedia_sdp_attr_create(pool, "ice-pwd", pj_cstr(&stmp, password)); media->attr[media->attr_count++] = attr; } it_candidates = ao2_iterator_init(candidates, 0); for (; (candidate = ao2_iterator_next(&it_candidates)); ao2_ref(candidate, -1)) { struct ast_str *attr_candidate = ast_str_create(128); ast_str_set(&attr_candidate, -1, "%s %u %s %d %s ", candidate->foundation, candidate->id, candidate->transport, candidate->priority, ast_sockaddr_stringify_addr_remote(&candidate->address)); ast_str_append(&attr_candidate, -1, "%s typ ", ast_sockaddr_stringify_port(&candidate->address)); switch (candidate->type) { case AST_RTP_ICE_CANDIDATE_TYPE_HOST: ast_str_append(&attr_candidate, -1, "host"); break; case AST_RTP_ICE_CANDIDATE_TYPE_SRFLX: ast_str_append(&attr_candidate, -1, "srflx"); break; case AST_RTP_ICE_CANDIDATE_TYPE_RELAYED: ast_str_append(&attr_candidate, -1, "relay"); break; } if (!ast_sockaddr_isnull(&candidate->relay_address)) { ast_str_append(&attr_candidate, -1, " raddr %s rport", ast_sockaddr_stringify_addr_remote(&candidate->relay_address)); ast_str_append(&attr_candidate, -1, " %s", ast_sockaddr_stringify_port(&candidate->relay_address)); } attr = pjmedia_sdp_attr_create(pool, "candidate", pj_cstr(&stmp, ast_str_buffer(attr_candidate))); media->attr[media->attr_count++] = attr; ast_free(attr_candidate); } ao2_iterator_destroy(&it_candidates); ao2_ref(candidates, -1); }
static inline int udptl_debug_test_addr(const struct ast_sockaddr *addr) { if (udptldebug == 0) return 0; if (ast_sockaddr_isnull(&udptldebugaddr)) { return 1; } if (ast_sockaddr_port(&udptldebugaddr)) { return !ast_sockaddr_cmp(&udptldebugaddr, addr); } else { return !ast_sockaddr_cmp_addr(&udptldebugaddr, addr); } }
int _ast_sockaddr_to_sin(const struct ast_sockaddr *addr, struct sockaddr_in *sin, const char *file, int line, const char *func) { if (ast_sockaddr_isnull(addr)) { memset(sin, 0, sizeof(*sin)); return 1; } if (addr->len != sizeof(*sin)) { ast_log(__LOG_ERROR, file, line, func, "Bad address cast to IPv4\n"); return 0; } if (addr->ss.ss_family != AF_INET && option_debug >= 1) { ast_log(__LOG_DEBUG, file, line, func, "Address family is not AF_INET\n"); } *sin = *(struct sockaddr_in *)&addr->ss; return 1; }
static int set_caps(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream) { RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, peer, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, joint, NULL, ao2_cleanup); enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT; int fmts = 0; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && ast_format_cap_count(session->direct_media_cap); int dsp_features = 0; if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); return -1; } /* get the endpoint capabilities */ if (direct_media_enabled) { ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps); format_cap_only_type(caps, media_type); } else { ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); } /* get the capabilities on the peer */ get_codecs(session, stream, &codecs, session_media); ast_rtp_codecs_payload_formats(&codecs, peer, &fmts); /* get the joint capabilities between peer and endpoint */ ast_format_cap_get_compatible(caps, peer, joint); if (!ast_format_cap_count(joint)) { struct ast_str *usbuf = ast_str_alloca(256); struct ast_str *thembuf = ast_str_alloca(256); ast_rtp_codecs_payloads_destroy(&codecs); ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n", session_media->stream_type, ast_format_cap_get_names(caps, &usbuf), ast_format_cap_get_names(peer, &thembuf)); return -1; } ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp), session_media->rtp); ast_format_cap_append_from_cap(session->req_caps, joint, AST_MEDIA_TYPE_UNKNOWN); if (session->channel) { ast_channel_lock(session->channel); ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN); ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_UNKNOWN); ast_format_cap_remove_by_type(caps, media_type); ast_format_cap_append_from_cap(caps, joint, media_type); /* * Apply the new formats to the channel, potentially changing * raw read/write formats and translation path while doing so. */ ast_channel_nativeformats_set(session->channel, caps); if (media_type == AST_MEDIA_TYPE_AUDIO) { ast_set_read_format(session->channel, ast_channel_readformat(session->channel)); ast_set_write_format(session->channel, ast_channel_writeformat(session->channel)); } if ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO) && (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833) && (session->dsp)) { dsp_features = ast_dsp_get_features(session->dsp); dsp_features &= ~DSP_FEATURE_DIGIT_DETECT; if (dsp_features) { ast_dsp_set_features(session->dsp, dsp_features); } else { ast_dsp_free(session->dsp); session->dsp = NULL; } } ast_channel_unlock(session->channel); } ast_rtp_codecs_payloads_destroy(&codecs); return 0; }
void ast_tcptls_server_start(struct ast_tcptls_session_args *desc) { int flags; int x = 1; /* Do nothing if nothing has changed */ if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) { ast_debug(1, "Nothing changed in %s\n", desc->name); return; } /* If we return early, there is no one listening */ ast_sockaddr_setnull(&desc->old_address); /* Shutdown a running server if there is one */ if (desc->master != AST_PTHREADT_NULL) { pthread_cancel(desc->master); pthread_kill(desc->master, SIGURG); pthread_join(desc->master, NULL); } if (desc->accept_fd != -1) { close(desc->accept_fd); } /* If there's no new server, stop here */ if (ast_sockaddr_isnull(&desc->local_address)) { ast_debug(2, "Server disabled: %s\n", desc->name); return; } desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ? AF_INET6 : AF_INET, SOCK_STREAM, 0); if (desc->accept_fd < 0) { ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno)); return; } setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); if (ast_bind(desc->accept_fd, &desc->local_address)) { ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n", desc->name, ast_sockaddr_stringify(&desc->local_address), strerror(errno)); goto error; } if (listen(desc->accept_fd, 10)) { ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); goto error; } flags = fcntl(desc->accept_fd, F_GETFL); fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n", desc->name, ast_sockaddr_stringify(&desc->local_address), strerror(errno)); goto error; } /* Set current info */ ast_sockaddr_copy(&desc->old_address, &desc->local_address); return; error: close(desc->accept_fd); desc->accept_fd = -1; }
static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata) { RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup); RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); struct request_transport_details details = { 0, }; pjsip_via_hdr *via = NULL; struct ast_sockaddr addr = { { 0, } }; pjsip_sip_uri *uri = NULL; RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup); /* If a transport selector is in use we know the transport or factory, so explicitly find it */ if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { details.transport = tdata->tp_sel.u.transport; } else if (tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) { details.factory = tdata->tp_sel.u.listener; } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) { /* Connectionless uses the same transport for all requests */ details.type = AST_TRANSPORT_UDP; details.transport = tdata->tp_info.transport; } else { if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TCP) { details.type = AST_TRANSPORT_TCP; } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TLS) { details.type = AST_TRANSPORT_TLS; } else { /* Unknown transport type, we can't map and thus can't apply NAT changes */ return PJ_SUCCESS; } if ((uri = nat_get_contact_sip_uri(tdata))) { details.local_address = uri->host; details.local_port = uri->port; } else if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) { details.local_address = via->sent_by.host; details.local_port = via->sent_by.port; } else { return PJ_SUCCESS; } if (!details.local_port) { details.local_port = (details.type == AST_TRANSPORT_TLS) ? 5061 : 5060; } } if (!(transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) || !(transport = ao2_callback(transports, 0, find_transport_in_use, &details)) || !transport->localnet || ast_sockaddr_isnull(&transport->external_address)) { return PJ_SUCCESS; } ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID); ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port); /* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */ if (ast_apply_ha(transport->localnet, &addr) != AST_SENSE_ALLOW) { return PJ_SUCCESS; } /* Update the contact header with the external address */ if (uri || (uri = nat_get_contact_sip_uri(tdata))) { pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address)); if (transport->external_signaling_port) { uri->port = transport->external_signaling_port; ast_debug(4, "Re-wrote Contact URI port to %d\n", uri->port); } } /* Update the via header if relevant */ if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) { pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport->external_address)); if (transport->external_signaling_port) { via->sent_by.port = transport->external_signaling_port; } } /* Invoke any additional hooks that may be registered */ if ((hooks = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "nat_hook", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) { struct nat_hook_details hook_details = { .tdata = tdata, .transport = transport, }; ao2_callback(hooks, 0, nat_invoke_hook, &hook_details); } return PJ_SUCCESS; }
static int set_caps(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream) { RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, peer, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, joint, NULL, ao2_cleanup); enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT; int fmts = 0; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && ast_format_cap_count(session->direct_media_cap); if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); return -1; } /* get the endpoint capabilities */ if (direct_media_enabled) { ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps); format_cap_only_type(caps, media_type); } else { ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); } /* get the capabilities on the peer */ get_codecs(session, stream, &codecs); ast_rtp_codecs_payload_formats(&codecs, peer, &fmts); /* get the joint capabilities between peer and endpoint */ ast_format_cap_get_compatible(caps, peer, joint); if (!ast_format_cap_count(joint)) { struct ast_str *usbuf = ast_str_alloca(64); struct ast_str *thembuf = ast_str_alloca(64); ast_rtp_codecs_payloads_destroy(&codecs); ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n", session_media->stream_type, ast_format_cap_get_names(caps, &usbuf), ast_format_cap_get_names(peer, &thembuf)); return -1; } ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp), session_media->rtp); ast_format_cap_append_from_cap(session->req_caps, joint, AST_MEDIA_TYPE_UNKNOWN); if (session->channel) { struct ast_format *fmt; ast_channel_lock(session->channel); ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN); ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_UNKNOWN); ast_format_cap_remove_by_type(caps, media_type); /* * XXX Historically we picked the "best" joint format to use * and stuck with it. It would be nice to just append the * determined joint media capabilities to give translation * more formats to choose from when necessary. Unfortunately, * there are some areas of the system where this doesn't work * very well. (The softmix bridge in particular is reluctant * to pick higher fidelity formats and has a problem with * asymmetric sample rates.) */ fmt = ast_format_cap_get_format(joint, 0); ast_format_cap_append(caps, fmt, 0); /* * Apply the new formats to the channel, potentially changing * raw read/write formats and translation path while doing so. */ ast_channel_nativeformats_set(session->channel, caps); ast_set_read_format(session->channel, ast_channel_readformat(session->channel)); ast_set_write_format(session->channel, ast_channel_writeformat(session->channel)); ast_channel_unlock(session->channel); ao2_ref(fmt, -1); } ast_rtp_codecs_payloads_destroy(&codecs); return 0; }
static int set_caps(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream) { RAII_VAR(struct ast_format_cap *, caps, NULL, ast_format_cap_destroy); RAII_VAR(struct ast_format_cap *, peer, NULL, ast_format_cap_destroy); RAII_VAR(struct ast_format_cap *, joint, NULL, ast_format_cap_destroy); enum ast_format_type media_type = stream_to_media_type(session_media->stream_type); struct ast_rtp_codecs codecs; struct ast_format fmt; int fmts = 0; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && !ast_format_cap_is_empty(session->direct_media_cap); if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK)) || !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK))) { ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); return -1; } /* get the endpoint capabilities */ if (direct_media_enabled) { ast_format_cap_joint_copy(session->endpoint->media.codecs, session->direct_media_cap, caps); } else { ast_format_cap_copy(caps, session->endpoint->media.codecs); } format_cap_only_type(caps, media_type); /* get the capabilities on the peer */ get_codecs(session, stream, &codecs); ast_rtp_codecs_payload_formats(&codecs, peer, &fmts); /* get the joint capabilities between peer and endpoint */ if (!(joint = ast_format_cap_joint(caps, peer))) { char usbuf[64], thembuf[64]; ast_rtp_codecs_payloads_destroy(&codecs); ast_getformatname_multiple(usbuf, sizeof(usbuf), caps); ast_getformatname_multiple(thembuf, sizeof(thembuf), peer); ast_log(LOG_WARNING, "No joint capabilities between our configuration(%s) and incoming SDP(%s)\n", usbuf, thembuf); return -1; } ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp), session_media->rtp); ast_format_cap_copy(caps, session->req_caps); ast_format_cap_remove_bytype(caps, media_type); ast_format_cap_append(caps, joint); ast_format_cap_append(session->req_caps, caps); if (session->channel) { ast_format_cap_copy(caps, ast_channel_nativeformats(session->channel)); ast_format_cap_remove_bytype(caps, media_type); ast_codec_choose(&session->endpoint->media.prefs, joint, 1, &fmt); ast_format_cap_add(caps, &fmt); /* Apply the new formats to the channel, potentially changing read/write formats while doing so */ ast_format_cap_copy(ast_channel_nativeformats(session->channel), caps); ast_set_read_format(session->channel, ast_channel_readformat(session->channel)); ast_set_write_format(session->channel, ast_channel_writeformat(session->channel)); } ast_rtp_codecs_payloads_destroy(&codecs); return 0; }
char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format) { struct ast_sockaddr sa_ipv4; const struct ast_sockaddr *sa_tmp; char host[NI_MAXHOST]; char port[NI_MAXSERV]; struct ast_str *str; int e; static const size_t size = sizeof(host) - 1 + sizeof(port) - 1 + 4; if (ast_sockaddr_isnull(sa)) { return "(null)"; } if (!(str = ast_str_thread_get(&ast_sockaddr_stringify_buf, size))) { return ""; } if (ast_sockaddr_ipv4_mapped(sa, &sa_ipv4)) { sa_tmp = &sa_ipv4; } else { sa_tmp = sa; } if ((e = getnameinfo((struct sockaddr *)&sa_tmp->ss, sa_tmp->len, format & AST_SOCKADDR_STR_ADDR ? host : NULL, format & AST_SOCKADDR_STR_ADDR ? sizeof(host) : 0, format & AST_SOCKADDR_STR_PORT ? port : 0, format & AST_SOCKADDR_STR_PORT ? sizeof(port): 0, NI_NUMERICHOST | NI_NUMERICSERV))) { ast_log(LOG_ERROR, "getnameinfo(): %s\n", gai_strerror(e)); return ""; } if ((format & AST_SOCKADDR_STR_REMOTE) == AST_SOCKADDR_STR_REMOTE) { char *p; if (ast_sockaddr_is_ipv6_link_local(sa) && (p = strchr(host, '%'))) { *p = '\0'; } } switch ((format & AST_SOCKADDR_STR_FORMAT_MASK)) { case AST_SOCKADDR_STR_DEFAULT: ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ? "[%s]:%s" : "%s:%s", host, port); break; case AST_SOCKADDR_STR_ADDR: ast_str_set(&str, 0, "%s", host); break; case AST_SOCKADDR_STR_HOST: ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ? "[%s]" : "%s", host); break; case AST_SOCKADDR_STR_PORT: ast_str_set(&str, 0, "%s", port); break; default: ast_log(LOG_ERROR, "Invalid format\n"); return ""; } return ast_str_buffer(str); }
void ast_tcptls_server_start(struct ast_tcptls_session_args *desc) { int flags; int x = 1; int tls_changed = 0; if (desc->tls_cfg) { char hash[41]; char *str = NULL; struct stat st; /* Store the hashes of the TLS certificate etc. */ if (stat(desc->tls_cfg->certfile, &st) || NULL == (str = ast_read_textfile(desc->tls_cfg->certfile))) { memset(hash, 0, 41); } else { ast_sha1_hash(hash, str); } ast_free(str); str = NULL; memcpy(desc->tls_cfg->certhash, hash, 41); if (stat(desc->tls_cfg->pvtfile, &st) || NULL == (str = ast_read_textfile(desc->tls_cfg->pvtfile))) { memset(hash, 0, 41); } else { ast_sha1_hash(hash, str); } ast_free(str); str = NULL; memcpy(desc->tls_cfg->pvthash, hash, 41); if (stat(desc->tls_cfg->cafile, &st) || NULL == (str = ast_read_textfile(desc->tls_cfg->cafile))) { memset(hash, 0, 41); } else { ast_sha1_hash(hash, str); } ast_free(str); str = NULL; memcpy(desc->tls_cfg->cahash, hash, 41); /* Check whether TLS configuration has changed */ if (!desc->old_tls_cfg) { /* No previous configuration */ tls_changed = 1; desc->old_tls_cfg = ast_calloc(1, sizeof(*desc->old_tls_cfg)); } else if (memcmp(desc->tls_cfg->certhash, desc->old_tls_cfg->certhash, 41)) { tls_changed = 1; } else if (memcmp(desc->tls_cfg->pvthash, desc->old_tls_cfg->pvthash, 41)) { tls_changed = 1; } else if (strcmp(desc->tls_cfg->cipher, desc->old_tls_cfg->cipher)) { tls_changed = 1; } else if (memcmp(desc->tls_cfg->cahash, desc->old_tls_cfg->cahash, 41)) { tls_changed = 1; } else if (strcmp(desc->tls_cfg->capath, desc->old_tls_cfg->capath)) { tls_changed = 1; } else if (memcmp(&desc->tls_cfg->flags, &desc->old_tls_cfg->flags, sizeof(desc->tls_cfg->flags))) { tls_changed = 1; } if (tls_changed) { ast_debug(1, "Changed parameters for %s found\n", desc->name); } } /* Do nothing if nothing has changed */ if (!tls_changed && !ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) { ast_debug(1, "Nothing changed in %s\n", desc->name); return; } /* If we return early, there is no one listening */ ast_sockaddr_setnull(&desc->old_address); /* Shutdown a running server if there is one */ if (desc->master != AST_PTHREADT_NULL) { pthread_cancel(desc->master); pthread_kill(desc->master, SIGURG); pthread_join(desc->master, NULL); } if (desc->accept_fd != -1) { close(desc->accept_fd); } /* If there's no new server, stop here */ if (ast_sockaddr_isnull(&desc->local_address)) { ast_debug(2, "Server disabled: %s\n", desc->name); return; } desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ? AF_INET6 : AF_INET, SOCK_STREAM, 0); if (desc->accept_fd < 0) { ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno)); return; } setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); if (ast_bind(desc->accept_fd, &desc->local_address)) { ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n", desc->name, ast_sockaddr_stringify(&desc->local_address), strerror(errno)); goto error; } if (listen(desc->accept_fd, 10)) { ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); goto error; } flags = fcntl(desc->accept_fd, F_GETFL); fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n", desc->name, ast_sockaddr_stringify(&desc->local_address), strerror(errno)); goto error; } /* Set current info */ ast_sockaddr_copy(&desc->old_address, &desc->local_address); if (desc->old_tls_cfg) { ast_free(desc->old_tls_cfg->certfile); ast_free(desc->old_tls_cfg->pvtfile); ast_free(desc->old_tls_cfg->cipher); ast_free(desc->old_tls_cfg->cafile); ast_free(desc->old_tls_cfg->capath); desc->old_tls_cfg->certfile = ast_strdup(desc->tls_cfg->certfile); desc->old_tls_cfg->pvtfile = ast_strdup(desc->tls_cfg->pvtfile); desc->old_tls_cfg->cipher = ast_strdup(desc->tls_cfg->cipher); desc->old_tls_cfg->cafile = ast_strdup(desc->tls_cfg->cafile); desc->old_tls_cfg->capath = ast_strdup(desc->tls_cfg->capath); memcpy(desc->old_tls_cfg->certhash, desc->tls_cfg->certhash, 41); memcpy(desc->old_tls_cfg->pvthash, desc->tls_cfg->pvthash, 41); memcpy(desc->old_tls_cfg->cahash, desc->tls_cfg->cahash, 41); memcpy(&desc->old_tls_cfg->flags, &desc->tls_cfg->flags, sizeof(desc->old_tls_cfg->flags)); } return; error: close(desc->accept_fd); desc->accept_fd = -1; }