Beispiel #1
0
/*!
 * \internal \brief Handle reading RTP information
 */
static int channel_read_rtp(struct ast_channel *chan, const char *type, const char *field, char *buf, size_t buflen)
{
	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
	struct chan_pjsip_pvt *pvt;
	struct ast_sip_session_media *media = NULL;
	struct ast_sockaddr addr;

	if (!channel) {
		ast_log(AST_LOG_WARNING, "Channel %s has no pvt!\n", ast_channel_name(chan));
		return -1;
	}

	pvt = channel->pvt;
	if (!pvt) {
		ast_log(AST_LOG_WARNING, "Channel %s has no chan_pjsip pvt!\n", ast_channel_name(chan));
		return -1;
	}

	if (ast_strlen_zero(type)) {
		ast_log(AST_LOG_WARNING, "You must supply a type field for 'rtp' information\n");
		return -1;
	}

	if (ast_strlen_zero(field) || !strcmp(field, "audio")) {
		media = pvt->media[SIP_MEDIA_AUDIO];
	} else if (!strcmp(field, "video")) {
		media = pvt->media[SIP_MEDIA_VIDEO];
	} else {
		ast_log(AST_LOG_WARNING, "Unknown media type field '%s' for 'rtp' information\n", field);
		return -1;
	}

	if (!media || !media->rtp) {
		ast_log(AST_LOG_WARNING, "Channel %s has no %s media/RTP session\n",
			ast_channel_name(chan), S_OR(field, "audio"));
		return -1;
	}

	if (!strcmp(type, "src")) {
		ast_rtp_instance_get_local_address(media->rtp, &addr);
		ast_copy_string(buf, ast_sockaddr_stringify(&addr), buflen);
	} else if (!strcmp(type, "dest")) {
		ast_rtp_instance_get_remote_address(media->rtp, &addr);
		ast_copy_string(buf, ast_sockaddr_stringify(&addr), buflen);
	} else if (!strcmp(type, "direct")) {
		ast_copy_string(buf, ast_sockaddr_stringify(&media->direct_media_addr), buflen);
	} else if (!strcmp(type, "secure")) {
		snprintf(buf, buflen, "%d", media->srtp ? 1 : 0);
	} else if (!strcmp(type, "hold")) {
		snprintf(buf, buflen, "%d", media->remotely_held ? 1 : 0);
	} else {
		ast_log(AST_LOG_WARNING, "Unknown type field '%s' specified for 'rtp' information\n", type);
		return -1;
	}

	return 0;
}
Beispiel #2
0
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;
}
/*!
 * \brief Pass WebSocket data into pjsip transport manager.
 */
static int transport_read(void *data)
{
	struct transport_read_data *read_data = data;
	struct ws_transport *newtransport = read_data->transport;
	struct ast_websocket *session = newtransport->ws_session;

	pjsip_rx_data *rdata = &newtransport->rdata;
	int recvd;
	pj_str_t buf;
	int pjsip_pkt_len;

	pj_gettimeofday(&rdata->pkt_info.timestamp);

	pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len;
	pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len);
	rdata->pkt_info.len = pjsip_pkt_len;
	rdata->pkt_info.zero = 0;

	pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
	rdata->pkt_info.src_addr.addr.sa_family = pj_AF_INET();

	rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr);

	pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_host(ast_websocket_remote_address(session)));
	rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session));

	recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata);

	pj_pool_reset(rdata->tp_info.pool);

	return (read_data->payload_len == recvd) ? 0 : -1;
}
Beispiel #4
0
static int apply_contact_acl(pjsip_rx_data *rdata, struct ast_acl_list *contact_acl)
{
	int num_contact_addrs;
	int forbidden = 0;
	struct ast_sockaddr *contact_addrs;
	int i;
	pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;

	if (ast_acl_list_is_empty(contact_acl)) {
		return 0;
	}

	while ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
		num_contact_addrs = extract_contact_addr(contact, &contact_addrs);
		if (num_contact_addrs <= 0) {
			continue;
		}
		for (i = 0; i < num_contact_addrs; ++i) {
			if (ast_apply_acl(contact_acl, &contact_addrs[i], "SIP Contact ACL: ") != AST_SENSE_ALLOW) {
				ast_log(LOG_WARNING, "Incoming SIP message from %s did not pass ACL test\n", ast_sockaddr_stringify(&contact_addrs[i]));
				forbidden = 1;
				break;
			}
		}
		ast_free(contact_addrs);
		if (forbidden) {
			/* No use checking other contacts if we already have failed ACL check */
			break;
		}
	}

	return forbidden;
}
/*! \brief Destructor function for sessions */
static void session_destroy_fn(void *obj)
{
	struct ast_websocket *session = obj;

	if (session->f) {
		fclose(session->f);
		ast_verb(2, "WebSocket connection from '%s' closed\n", ast_sockaddr_stringify(&session->address));
	}

	ast_free(session->payload);
}
Beispiel #6
0
static int apply_acl(pjsip_rx_data *rdata, struct ast_acl_list *acl)
{
	struct ast_sockaddr addr;

	if (ast_acl_list_is_empty(acl)) {
		return 0;
	}

	memset(&addr, 0, sizeof(addr));
	ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
	ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);

	if (ast_apply_acl(acl, &addr, "SIP ACL: ") != AST_SENSE_ALLOW) {
		ast_log(LOG_WARNING, "Incoming SIP message from %s did not pass ACL test\n", ast_sockaddr_stringify(&addr));
		return 1;
	}
	return 0;
}
Beispiel #7
0
static void dump_addr(char *output, int maxlen, void *value, int len)
{
	struct ast_sockaddr addr;

	if (len == (int)sizeof(struct sockaddr_in)) {
		addr.ss.ss_family = AF_INET;
	} else if (len == (int) sizeof(struct sockaddr_in6)) {
		addr.ss.ss_family = AF_INET6;
	} else {
		ast_copy_string(output, "Invalid Address", maxlen);
		return;
	}

	memcpy(&addr, value, len);
	addr.len = len;

	snprintf(output, maxlen, "%s %s",
				ast_sockaddr_is_ipv4(&addr) || ast_sockaddr_is_ipv4_mapped(&addr) ? "IPV4" : "IPV6",
				ast_sockaddr_stringify(&addr));
}
Beispiel #8
0
struct ast_tcptls_session_instance *ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
{
	struct ast_tcptls_session_args *desc;
	int flags;

	if (!(desc = tcptls_session->parent)) {
		goto client_start_error;
	}

	if (ast_connect(desc->accept_fd, &desc->remote_address)) {
		ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
			desc->name,
			ast_sockaddr_stringify(&desc->remote_address),
			strerror(errno));
		goto client_start_error;
	}

	flags = fcntl(desc->accept_fd, F_GETFL);
	fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);

	if (desc->tls_cfg) {
		desc->tls_cfg->enabled = 1;
		__ssl_setup(desc->tls_cfg, 1);
	}

	return handle_tcptls_connection(tcptls_session);

client_start_error:
	if (desc) {
		close(desc->accept_fd);
		desc->accept_fd = -1;
	}
	ao2_ref(tcptls_session, -1);
	return NULL;

}
/*!
 * \brief Create a pjsip transport.
 */
static int transport_create(void *data)
{
	struct transport_create_data *create_data = data;
	struct ws_transport *newtransport = NULL;

	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
	struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);

	pj_pool_t *pool;
	pj_str_t buf;
	pj_status_t status;

	newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor,
			AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport");
	if (!newtransport) {
		ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
		goto on_error;
	}

	newtransport->transport.endpt = endpt;

	if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
		ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
		goto on_error;
	}

	newtransport->transport.pool = pool;
	newtransport->ws_session = create_data->ws_session;

	/* Keep the session until transport dies */
	ast_websocket_ref(newtransport->ws_session);

	status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
	if (status != PJ_SUCCESS) {
		goto on_error;
	}

	status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
	if (status != PJ_SUCCESS) {
		goto on_error;
	}

	pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session))), &newtransport->transport.key.rem_addr);
	newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET();
	newtransport->transport.key.type = ast_websocket_is_secure(newtransport->ws_session) ? transport_type_wss : transport_type_ws;

	newtransport->transport.addr_len = pj_sockaddr_get_len(&newtransport->transport.key.rem_addr);

	pj_sockaddr_cp(&newtransport->transport.local_addr, &newtransport->transport.key.rem_addr);

	newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, newtransport->transport.addr_len+4);
	pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, newtransport->transport.addr_len+4, 0);
	newtransport->transport.local_name.host.slen = pj_ansi_strlen(newtransport->transport.local_name.host.ptr);
	newtransport->transport.local_name.port = pj_sockaddr_get_port(&newtransport->transport.key.rem_addr);

	newtransport->transport.type_name = (char *)pjsip_transport_get_type_name(newtransport->transport.key.type);
	newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
	newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);

	newtransport->transport.tpmgr = tpmgr;
	newtransport->transport.send_msg = &ws_send_msg;
	newtransport->transport.destroy = &ws_destroy;

	status = pjsip_transport_register(newtransport->transport.tpmgr,
			(pjsip_transport *)newtransport);
	if (status != PJ_SUCCESS) {
		goto on_error;
	}

	/* Add a reference for pjsip transport manager */
	ao2_ref(newtransport, +1);

	newtransport->rdata.tp_info.transport = &newtransport->transport;
	newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p",
		PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
	if (!newtransport->rdata.tp_info.pool) {
		ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n");
		pjsip_transport_destroy((pjsip_transport *)newtransport);
		goto on_error;
	}

	create_data->transport = newtransport;
	return 0;

on_error:
	ao2_cleanup(newtransport);
	return -1;
}
Beispiel #10
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;
}
Beispiel #11
0
/* \brief called by scheduler to send STUN request */
static int stun_monitor_request(const void *blarg)
{
	int res;
	struct sockaddr_in answer;
	static const struct sockaddr_in no_addr = { 0, };

	ast_mutex_lock(&args.lock);
	if (!args.monitor_enabled) {
		goto monitor_request_cleanup;
	}

	if (args.stun_sock < 0) {
		struct ast_sockaddr stun_addr;

		/* STUN socket not open.  Refresh the server DNS address resolution. */
		if (!args.server_hostname) {
			/* No STUN hostname? */
			goto monitor_request_cleanup;
		}

		/* Lookup STUN address. */
		memset(&stun_addr, 0, sizeof(stun_addr));
		stun_addr.ss.ss_family = AF_INET;
		if (ast_get_ip(&stun_addr, args.server_hostname)) {
			/* Lookup failed. */
			ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
				args.server_hostname);
			goto monitor_request_cleanup;
		}
		ast_sockaddr_set_port(&stun_addr, args.stun_port);

		/* open socket binding */
		args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
		if (args.stun_sock < 0) {
			ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
			goto monitor_request_cleanup;
		}
		if (ast_connect(args.stun_sock, &stun_addr)) {
			ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
				ast_sockaddr_stringify(&stun_addr), strerror(errno));
			stun_close_sock();
			goto monitor_request_cleanup;
		}
	}

	res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
	if (res) {
		/*
		 * STUN request timed out or errored.
		 *
		 * Refresh the server DNS address resolution next time around.
		 */
		if (!args.stun_poll_failed_gripe) {
			args.stun_poll_failed_gripe = 1;
			ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
				res < 0 ? "failed" : "got no response");
		}
		stun_close_sock();
	} else {
		args.stun_poll_failed_gripe = 0;
		if (memcmp(&no_addr, &answer, sizeof(no_addr))
			&& memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
			const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
			int newport = ntohs(answer.sin_port);

			ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
				ast_inet_ntoa(args.external_addr.sin_addr),
				ntohs(args.external_addr.sin_port), newaddr, newport);

			args.external_addr = answer;

			if (args.external_addr_known) {
				struct ast_event *event;

				/*
				 * The external address was already known, and has changed...
				 * generate event.
				 */
				event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
				if (!event) {
					ast_log(LOG_ERROR, "Could not create AST_EVENT_NETWORK_CHANGE event.\n");
				} else if (ast_event_queue(event)) {
					ast_event_destroy(event);
					ast_log(LOG_ERROR, "Could not queue AST_EVENT_NETWORK_CHANGE event.\n");
				}
			} else {
				/* this was the first external address we found, do not alert listeners
				 * until this address changes to something else. */
				args.external_addr_known = 1;
			}
		}
	}

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;
}
Beispiel #12
0
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;
}