Esempio n. 1
0
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
{
	struct distributor_dialog_data *dist;

	ao2_wrlock(dialog_associations);
	dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
	if (!dist) {
		if (endpoint) {
			dist = ao2_alloc(sizeof(*dist), NULL);
			if (dist) {
				dist->dlg = dlg;
				dist->endpoint = endpoint;
				ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
				ao2_ref(dist, -1);
			}
		}
	} else {
		ao2_lock(dist);
		dist->endpoint = endpoint;
		if (!dist->serializer && !dist->endpoint) {
			ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
		}
		ao2_unlock(dist);
		ao2_ref(dist, -1);
	}
	ao2_unlock(dialog_associations);
}
Esempio n. 2
0
int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
{
	if (formatter) {
		ao2_wrlock(formatter_registry);
		if (ao2_ref(formatter, -1) == 2) {
			ao2_unlink_flags(formatter_registry, formatter, OBJ_NOLOCK);
		}
		ao2_unlock(formatter_registry);
	}
	return 0;
}
Esempio n. 3
0
static void check_endpoint(pjsip_rx_data *rdata, struct unidentified_request *unid,
	const char *name)
{
	int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);

	ao2_wrlock(unid);
	unid->count++;

	if (ms < (unidentified_period * 1000) && unid->count >= unidentified_count) {
		log_failed_request(rdata, "No matching endpoint found", unid->count, ms);
		ast_sip_report_invalid_endpoint(name, rdata);
	}
	ao2_unlock(unid);
}
Esempio n. 4
0
struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
{
	struct ao2_container *contacts;
	struct ast_named_lock *lock;

	lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", ast_sorcery_object_get_id(aor));
	if (!lock) {
		return NULL;
	}

	ao2_wrlock(lock);
	contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
	ao2_unlock(lock);
	ast_named_lock_put(lock);

	return contacts;
}
Esempio n. 5
0
/*!
 * \internal
 * \brief Adjust an object's lock to the requested level.
 *
 * \param user_data An ao2 object to adjust lock level.
 * \param lock_how What level to adjust lock.
 * \param keep_stronger TRUE if keep original lock level if it is stronger.
 *
 * \pre The ao2 object is already locked.
 *
 * \details
 * An ao2 object with a RWLOCK will have its lock level adjusted
 * to the specified level if it is not already there.  An ao2
 * object with a different type of lock is not affected.
 *
 * \return Original lock level.
 */
enum ao2_lock_req __adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger)
{
	struct astobj2 *obj = INTERNAL_OBJ(user_data);
	struct astobj2_rwlock *obj_rwlock;
	enum ao2_lock_req orig_lock;

	switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
	case AO2_ALLOC_OPT_LOCK_RWLOCK:
		obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
		if (obj_rwlock->rwlock.num_lockers < 0) {
			orig_lock = AO2_LOCK_REQ_WRLOCK;
		} else {
			orig_lock = AO2_LOCK_REQ_RDLOCK;
		}
		switch (lock_how) {
		case AO2_LOCK_REQ_MUTEX:
			lock_how = AO2_LOCK_REQ_WRLOCK;
			/* Fall through */
		case AO2_LOCK_REQ_WRLOCK:
			if (lock_how != orig_lock) {
				/* Switch from read lock to write lock. */
				ao2_unlock(user_data);
				ao2_wrlock(user_data);
			}
			break;
		case AO2_LOCK_REQ_RDLOCK:
			if (!keep_stronger && lock_how != orig_lock) {
				/* Switch from write lock to read lock. */
				ao2_unlock(user_data);
				ao2_rdlock(user_data);
			}
			break;
		}
		break;
	default:
		ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
		/* Fall through */
	case AO2_ALLOC_OPT_LOCK_NOLOCK:
	case AO2_ALLOC_OPT_LOCK_MUTEX:
		orig_lock = AO2_LOCK_REQ_MUTEX;
		break;
	}

	return orig_lock;
}
Esempio n. 6
0
int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
		struct timeval expiration_time, const char *path_info, const char *user_agent,
		const char *via_addr, int via_port, const char *call_id,
		struct ast_sip_endpoint *endpoint)
{
	int res;
	struct ast_named_lock *lock;

	lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", ast_sorcery_object_get_id(aor));
	if (!lock) {
		return -1;
	}

	ao2_wrlock(lock);
	res = ast_sip_location_add_contact_nolock(aor, uri, expiration_time, path_info, user_agent,
		via_addr, via_port, call_id,
		endpoint);
	ao2_unlock(lock);
	ast_named_lock_put(lock);

	return res;
}
Esempio n. 7
0
/*! \brief Apply handler for transports */
static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
{
	struct ast_sip_transport *transport = obj;
	const char *transport_id = ast_sorcery_object_get_id(obj);
	RAII_VAR(struct ao2_container *, states, transport_states, states_cleanup);
	RAII_VAR(struct internal_state *, temp_state, NULL, ao2_cleanup);
	RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
	RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
	pj_status_t res = -1;
	int i;
#define BIND_TRIES 3
#define BIND_DELAY_US 100000

	if (!states) {
		return -1;
	}

	/*
	 * transport_apply gets called for EVERY retrieval of a transport when using realtime.
	 * We need to prevent multiple threads from trying to mess with underlying transports
	 * at the same time.  The container is the only thing we have to lock on.
	 */
	ao2_wrlock(states);

	temp_state = internal_state_alloc(transport);
	if (!temp_state) {
		ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
		return -1;
	}

	perm_state = find_internal_state_by_transport(transport);
	if (perm_state) {
		ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes);
		if (!changes && !has_state_changed(perm_state->state, temp_state->state)) {
			/* In case someone is using the deprecated fields, reset them */
			transport->state = perm_state->state;
			copy_state_to_transport(transport);
			ao2_replace(perm_state->transport, transport);
			return 0;
		}

		if (!transport->allow_reload) {
			if (!perm_state->change_detected) {
				perm_state->change_detected = 1;
				ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);
			}
			/* In case someone is using the deprecated fields, reset them */
			transport->state = perm_state->state;
			copy_state_to_transport(transport);
			ao2_replace(perm_state->transport, transport);
			return 0;
		}
	}

	if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {
		ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);
		return -1;
	}

	/* Set default port if not present */
	if (!pj_sockaddr_get_port(&temp_state->state->host)) {
		pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);
	}

	/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
	if (!ast_strlen_zero(transport->external_signaling_address)) {
		if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
			temp_state->state->external_address.ss.ss_family = AF_INET;
		} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
			temp_state->state->external_address.ss.ss_family = AF_INET6;
		} else {
			ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
					transport_id);
			return -1;
		}

		if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
			ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
			return -1;
		}
	}

	if (transport->type == AST_TRANSPORT_UDP) {

		for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
			if (perm_state && perm_state->state && perm_state->state->transport) {
				pjsip_udp_transport_pause(perm_state->state->transport,
					PJSIP_UDP_TRANSPORT_DESTROY_SOCKET);
				usleep(BIND_DELAY_US);
			}

			if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
				res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(),
					&temp_state->state->host.ipv4, NULL, transport->async_operations,
					&temp_state->state->transport);
			} else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
				res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(),
					&temp_state->state->host.ipv6, NULL, transport->async_operations,
					&temp_state->state->transport);
			}
		}

		if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
			pj_sock_t sock;
			pj_qos_params qos_params;
			sock = pjsip_udp_transport_get_socket(temp_state->state->transport);
			pj_sock_get_qos_params(sock, &qos_params);
			set_qos(transport, &qos_params);
			pj_sock_set_qos_params(sock, &qos_params);
		}
	} else if (transport->type == AST_TRANSPORT_TCP) {
		pjsip_tcp_transport_cfg cfg;

		pjsip_tcp_transport_cfg_default(&cfg, temp_state->state->host.addr.sa_family);
		cfg.bind_addr = temp_state->state->host;
		cfg.async_cnt = transport->async_operations;
		set_qos(transport, &cfg.qos_params);

		for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
			if (perm_state && perm_state->state && perm_state->state->factory
				&& perm_state->state->factory->destroy) {
				perm_state->state->factory->destroy(perm_state->state->factory);
				usleep(BIND_DELAY_US);
			}

			res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
				&temp_state->state->factory);
		}
	} else if (transport->type == AST_TRANSPORT_TLS) {
		if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
			ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
					ast_sorcery_object_get_id(obj));
			return -1;
		}

		temp_state->state->tls.password = pj_str((char*)transport->password);
		set_qos(transport, &temp_state->state->tls.qos_params);

		for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
			if (perm_state && perm_state->state && perm_state->state->factory
				&& perm_state->state->factory->destroy) {
				perm_state->state->factory->destroy(perm_state->state->factory);
				usleep(BIND_DELAY_US);
			}

			res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
				&temp_state->state->host, NULL, transport->async_operations,
				&temp_state->state->factory);
		}
	} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
		if (transport->cos || transport->tos) {
			ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
		}
		res = PJ_SUCCESS;
	}

	if (res != PJ_SUCCESS) {
		char msg[PJ_ERR_MSG_SIZE];

		pj_strerror(res, msg, sizeof(msg));
		ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
		return -1;
	}

	copy_state_to_transport(transport);
	if (perm_state) {
		ao2_unlink_flags(states, perm_state, OBJ_NOLOCK);
	}
	ao2_link_flags(states, temp_state, OBJ_NOLOCK);

	return 0;
}
Esempio n. 8
0
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
{
	struct ast_sip_endpoint *endpoint;
	struct unidentified_request *unid;
	int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;

	endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
	if (endpoint) {
		/*
		 * ao2_find with OBJ_UNLINK always write locks the container before even searching
		 * for the object.  Since the majority case is that the object won't be found, do
		 * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink
		 * if needed.
		 */
		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			ao2_unlink(unidentified_requests, unid);
			ao2_ref(unid, -1);
		}
		apply_acls(rdata);
		return PJ_FALSE;
	}

	endpoint = ast_sip_identify_endpoint(rdata);
	if (endpoint) {
		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			ao2_unlink(unidentified_requests, unid);
			ao2_ref(unid, -1);
		}
	}

	if (!endpoint) {
		/* always use an artificial endpoint - per discussion no reason
		   to have "alwaysauthreject" as an option.  It is felt using it
		   was a bug fix and it is not needed since we are not worried about
		   breaking old stuff and we really don't want to enable the discovery
		   of SIP accounts */
		endpoint = ast_sip_get_artificial_endpoint();
	}

	/* endpoint ref held by mod_data[] */
	rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint;

	if (endpoint == artificial_endpoint && !is_ack) {
		char name[AST_UUID_STR_LEN] = "";
		pjsip_uri *from = rdata->msg_info.from->uri;

		if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) {
			pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from);
			ast_copy_pj_str(name, &sip_from->user, sizeof(name));
		}

		unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY);
		if (unid) {
			check_endpoint(rdata, unid, name);
			ao2_ref(unid, -1);
		} else if (using_auth_username) {
			ao2_wrlock(unidentified_requests);
			/* Checking again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */
			unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name,
				OBJ_SEARCH_KEY | OBJ_NOLOCK);
			if (unid) {
				check_endpoint(rdata, unid, name);
			} else {
				unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1,
					NULL, AO2_ALLOC_OPT_LOCK_RWLOCK);
				if (!unid) {
					ao2_unlock(unidentified_requests);
					pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
					return PJ_TRUE;
				}
				strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */
				unid->first_seen = ast_tvnow();
				unid->count = 1;
				ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK);
			}
			ao2_ref(unid, -1);
			ao2_unlock(unidentified_requests);
		} else {
			log_failed_request(rdata, "No matching endpoint found", 0, 0);
			ast_sip_report_invalid_endpoint(name, rdata);
		}
	}

	apply_acls(rdata);
	return PJ_FALSE;
}