bool qd_policy_approve_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn) { bool result = true; if (qd_conn->policy_settings) { if (qd_conn->policy_settings->maxSessions) { if (qd_conn->n_sessions == qd_conn->policy_settings->maxSessions) { qd_policy_deny_amqp_session(ssn, qd_conn); result = false; } } } pn_connection_t *conn = qd_connection_pn(qd_conn); qd_dispatch_t *qd = qd_conn->server->qd; qd_policy_t *policy = qd->policy; const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr); const char *app = pn_connection_remote_hostname(conn); if (result) { qd_log(policy->log_source, QD_LOG_TRACE, "ALLOW AMQP Begin Session. user: %s, hostip: %s, app: %s", qd_conn->user_id, hostip, app); } else { qd_log(policy->log_source, QD_LOG_INFO, "DENY AMQP Begin Session due to session limit. user: %s, hostip: %s, app: %s", qd_conn->user_id, hostip, app); } return result; }
qd_link_t *qd_link(qd_node_t *node, qd_connection_t *conn, qd_direction_t dir, const char* name) { qd_link_t *link = new_qd_link_t(); link->pn_sess = pn_session(qd_connection_pn(conn)); pn_session_set_incoming_capacity(link->pn_sess, 1000000); if (dir == QD_OUTGOING) link->pn_link = pn_sender(link->pn_sess, name); else link->pn_link = pn_receiver(link->pn_sess, name); link->direction = dir; link->context = node->context; link->node = node; link->drain_mode = pn_link_get_drain(link->pn_link); link->remote_snd_settle_mode = pn_link_remote_snd_settle_mode(link->pn_link); link->close_sess_with_link = true; // // Keep the borrowed references // pn_incref(link->pn_link); pn_incref(link->pn_sess); pn_link_set_context(link->pn_link, link); pn_session_open(link->pn_sess); return link; }
bool qd_policy_approve_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_conn) { const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr); const char *app = pn_connection_remote_hostname(qd_connection_pn(qd_conn)); if (qd_conn->policy_settings->maxReceivers) { if (qd_conn->n_receivers == qd_conn->policy_settings->maxReceivers) { // Max sender limit specified and violated. qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_INFO, "DENY AMQP Attach receiver for user '%s', host '%s', app '%s' based on maxReceivers limit", qd_conn->user_id, hostip, app); _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn); return false; } else { // max receiver limit not violated } } else { // max receiver limit not specified } // Approve receiver link based on source bool dynamic_src = pn_terminus_is_dynamic(pn_link_remote_source(pn_link)); if (dynamic_src) { bool lookup = qd_conn->policy_settings->allowDynamicSrc; qd_log(qd_conn->server->qd->policy->log_source, (lookup ? QD_LOG_TRACE : QD_LOG_INFO), "%s AMQP Attach receiver dynamic source for user '%s', host '%s', app '%s',", (lookup ? "ALLOW" : "DENY"), qd_conn->user_id, hostip, app); // Dynamic source policy rendered the decision if (!lookup) { _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn); } return lookup; } const char * source = pn_terminus_get_address(pn_link_remote_source(pn_link)); if (source && *source) { // a source is specified bool lookup = _qd_policy_approve_link_name(qd_conn->user_id, qd_conn->policy_settings->sources, source); qd_log(qd_conn->server->qd->policy->log_source, (lookup ? QD_LOG_TRACE : QD_LOG_INFO), "%s AMQP Attach receiver link '%s' for user '%s', host '%s', app '%s' based on link source name", (lookup ? "ALLOW" : "DENY"), source, qd_conn->user_id, hostip, app); if (!lookup) { _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn); return false; } } else { // A receiver with no remote source. qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_TRACE, "DENY AMQP Attach receiver link '' for user '%s', host '%s', app '%s'", qd_conn->user_id, hostip, app); _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn); return false; } // Approved return true; }
static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool inbound) { qdr_connection_role_t role = 0; int cost = 1; int remote_cost = 1; bool strip_annotations_in = false; bool strip_annotations_out = false; int link_capacity = 1; const char *name = 0; uint64_t connection_id = qd_connection_connection_id(conn); pn_connection_t *pn_conn = qd_connection_pn(conn); qd_router_connection_get_config(conn, &role, &cost, &name, &strip_annotations_in, &strip_annotations_out, &link_capacity); if (role == QDR_ROLE_INTER_ROUTER) { // // Check the remote properties for an inter-router cost value. // pn_data_t *props = pn_conn ? pn_connection_remote_properties(pn_conn) : 0; if (props) { pn_data_rewind(props); pn_data_next(props); if (props && pn_data_type(props) == PN_MAP) { pn_data_enter(props); while (pn_data_next(props)) { if (pn_data_type(props) == PN_SYMBOL) { pn_bytes_t sym = pn_data_get_symbol(props); if (sym.size == strlen(QD_CONNECTION_PROPERTY_COST_KEY) && strcmp(sym.start, QD_CONNECTION_PROPERTY_COST_KEY) == 0) { pn_data_next(props); if (pn_data_type(props) == PN_INT) remote_cost = pn_data_get_int(props); break; } } } } } // // Use the larger of the local and remote costs for this connection // if (remote_cost > cost) cost = remote_cost; } qdr_connection_t *qdrc = qdr_connection_opened(router->router_core, inbound, role, cost, connection_id, name, pn_connection_remote_container(pn_conn), strip_annotations_in, strip_annotations_out, link_capacity); qd_connection_set_context(conn, qdrc); qdr_connection_set_context(qdrc, conn); }
void qd_policy_amqp_open(void *context, bool discard) { qd_connection_t *qd_conn = (qd_connection_t *)context; if (!discard) { pn_connection_t *conn = qd_connection_pn(qd_conn); qd_dispatch_t *qd = qd_conn->server->qd; qd_policy_t *policy = qd->policy; bool connection_allowed = true; if (policy->enableAccessRules) { // Open connection or not based on policy. pn_transport_t *pn_trans = pn_connection_transport(conn); const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr); const char *pcrh = pn_connection_remote_hostname(conn); const char *app = (pcrh ? pcrh : ""); const char *conn_name = qdpn_connector_name(qd_conn->pn_cxtr); #define SETTINGS_NAME_SIZE 256 char settings_name[SETTINGS_NAME_SIZE]; uint32_t conn_id = qd_conn->connection_id; qd_conn->policy_settings = NEW(qd_policy_settings_t); // TODO: memory pool for settings memset(qd_conn->policy_settings, 0, sizeof(qd_policy_settings_t)); if (qd_policy_open_lookup_user(policy, qd_conn->user_id, hostip, app, conn_name, settings_name, SETTINGS_NAME_SIZE, conn_id, qd_conn->policy_settings) && settings_name[0]) { // This connection is allowed by policy. // Apply transport policy settings if (qd_conn->policy_settings->maxFrameSize > 0) pn_transport_set_max_frame(pn_trans, qd_conn->policy_settings->maxFrameSize); if (qd_conn->policy_settings->maxSessions > 0) pn_transport_set_channel_max(pn_trans, qd_conn->policy_settings->maxSessions - 1); } else { // This connection is denied by policy. connection_allowed = false; } } else { // No policy implies automatic policy allow // Note that connections not governed by policy have no policy_settings. } if (connection_allowed) { if (pn_connection_state(conn) & PN_LOCAL_UNINIT) pn_connection_open(conn); policy_notify_opened(qd_conn->open_container, qd_conn, qd_conn->context); } else { qd_policy_private_deny_amqp_connection(conn, RESOURCE_LIMIT_EXCEEDED, CONNECTION_DISALLOWED); } } qd_connection_set_event_stall(qd_conn, false); }
bool qd_policy_approve_amqp_sender_link(pn_link_t *pn_link, qd_connection_t *qd_conn) { const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr); const char *app = pn_connection_remote_hostname(qd_connection_pn(qd_conn)); if (qd_conn->policy_settings->maxSenders) { if (qd_conn->n_senders == qd_conn->policy_settings->maxSenders) { // Max sender limit specified and violated. qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_INFO, "DENY AMQP Attach sender for user '%s', host '%s', app '%s' based on maxSenders limit", qd_conn->user_id, hostip, app); _qd_policy_deny_amqp_sender_link(pn_link, qd_conn); return false; } else { // max sender limit not violated } } else { // max sender limit not specified } // Approve sender link based on target const char * target = pn_terminus_get_address(pn_link_remote_target(pn_link)); bool lookup; if (target && *target) { // a target is specified lookup = _qd_policy_approve_link_name(qd_conn->user_id, qd_conn->policy_settings->targets, target); qd_log(qd_conn->server->qd->policy->log_source, (lookup ? QD_LOG_TRACE : QD_LOG_INFO), "%s AMQP Attach sender link '%s' for user '%s', host '%s', app '%s' based on link target name", (lookup ? "ALLOW" : "DENY"), target, qd_conn->user_id, hostip, app); if (!lookup) { _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn); return false; } } else { // A sender with no remote target. // This happens all the time with anonymous relay lookup = qd_conn->policy_settings->allowAnonymousSender; qd_log(qd_conn->server->qd->policy->log_source, (lookup ? QD_LOG_TRACE : QD_LOG_INFO), "%s AMQP Attach anonymous sender for user '%s', host '%s', app '%s'", (lookup ? "ALLOW" : "DENY"), qd_conn->user_id, hostip, app); if (!lookup) { _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn); return false; } } // Approved return true; }
static int handler(void *handler_context, void *conn_context, qd_conn_event_t event, qd_connection_t *qd_conn) { qd_container_t *container = (qd_container_t*) handler_context; pn_connection_t *conn = qd_connection_pn(qd_conn); switch (event) { case QD_CONN_EVENT_LISTENER_OPEN: open_handler(container, qd_conn, QD_INCOMING, conn_context); return 1; case QD_CONN_EVENT_CONNECTOR_OPEN: open_handler(container, qd_conn, QD_OUTGOING, conn_context); return 1; case QD_CONN_EVENT_CLOSE: return close_handler(container, conn_context, conn, qd_conn); case QD_CONN_EVENT_WRITABLE: return writable_handler(container, conn, qd_conn); } return 0; }
int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *event, qd_connection_t *qd_conn) { qd_container_t *container = (qd_container_t*) handler_context; pn_connection_t *conn = qd_connection_pn(qd_conn); pn_session_t *ssn; pn_link_t *pn_link; qd_link_t *qd_link; pn_delivery_t *delivery; switch (pn_event_type(event)) { case PN_CONNECTION_REMOTE_OPEN : qd_connection_set_user(qd_conn); if (pn_connection_state(conn) & PN_LOCAL_UNINIT) { // This Open is an externally initiated connection // Let policy engine decide qd_connection_set_event_stall(qd_conn, true); qd_conn->open_container = (void *)container; qd_connection_invoke_deferred(qd_conn, qd_policy_amqp_open, qd_conn); } else { // This Open is in response to an internally initiated connection notify_opened(container, qd_conn, conn_context); } break; case PN_CONNECTION_REMOTE_CLOSE : if (pn_connection_state(conn) == (PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED)) pn_connection_close(conn); break; case PN_SESSION_REMOTE_OPEN : if (!(pn_connection_state(conn) & PN_LOCAL_CLOSED)) { ssn = pn_event_session(event); if (pn_session_state(ssn) & PN_LOCAL_UNINIT) { if (qd_conn->policy_settings) { if (!qd_policy_approve_amqp_session(ssn, qd_conn)) { break; } qd_conn->n_sessions++; } qd_policy_apply_session_settings(ssn, qd_conn); pn_session_open(ssn); } } break; case PN_SESSION_REMOTE_CLOSE : if (!(pn_connection_state(conn) & PN_LOCAL_CLOSED)) { ssn = pn_event_session(event); if (pn_session_state(ssn) == (PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED)) { // remote has nuked our session. Check for any links that were // left open and forcibly detach them, since no detaches will // arrive on this session. pn_connection_t *conn = pn_session_connection(ssn); pn_link_t *pn_link = pn_link_head(conn, PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE); while (pn_link) { if (pn_link_session(pn_link) == ssn) { qd_link_t *qd_link = (qd_link_t*) pn_link_get_context(pn_link); if (qd_link && qd_link->node) { if (qd_conn->policy_settings) { if (qd_link->direction == QD_OUTGOING) { qd_conn->n_receivers--; assert(qd_conn->n_receivers >= 0); } else { qd_conn->n_senders--; assert(qd_conn->n_senders >= 0); } } qd_log(container->log_source, QD_LOG_NOTICE, "Aborting link '%s' due to parent session end", pn_link_name(pn_link)); qd_link->node->ntype->link_detach_handler(qd_link->node->context, qd_link, QD_LOST); } } pn_link = pn_link_next(pn_link, PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE); } if (qd_conn->policy_settings) { qd_conn->n_sessions--; } pn_session_close(ssn); } } break; case PN_LINK_REMOTE_OPEN : if (!(pn_connection_state(conn) & PN_LOCAL_CLOSED)) { pn_link = pn_event_link(event); if (pn_link_state(pn_link) & PN_LOCAL_UNINIT) { if (pn_link_is_sender(pn_link)) { if (qd_conn->policy_settings) { if (!qd_policy_approve_amqp_receiver_link(pn_link, qd_conn)) { break; } qd_conn->n_receivers++; } setup_outgoing_link(container, pn_link); } else { if (qd_conn->policy_settings) { if (!qd_policy_approve_amqp_sender_link(pn_link, qd_conn)) { break; } qd_conn->n_senders++; } setup_incoming_link(container, pn_link); } } else if (pn_link_state(pn_link) & PN_LOCAL_ACTIVE) handle_link_open(container, pn_link); } break; case PN_LINK_REMOTE_CLOSE : case PN_LINK_REMOTE_DETACH : if (!(pn_connection_state(conn) & PN_LOCAL_CLOSED)) { pn_link = pn_event_link(event); qd_link = (qd_link_t*) pn_link_get_context(pn_link); if (qd_link) { qd_node_t *node = qd_link->node; qd_detach_type_t dt = pn_event_type(event) == PN_LINK_REMOTE_CLOSE ? QD_CLOSED : QD_DETACHED; if (node) node->ntype->link_detach_handler(node->context, qd_link, dt); else if (qd_link->pn_link == pn_link) { pn_link_close(pn_link); } if (qd_conn->policy_counted && qd_conn->policy_settings) { if (pn_link_is_sender(pn_link)) { qd_conn->n_receivers--; qd_log(container->log_source, QD_LOG_TRACE, "Closed receiver link %s. n_receivers: %d", pn_link_name(pn_link), qd_conn->n_receivers); assert (qd_conn->n_receivers >= 0); } else { qd_conn->n_senders--; qd_log(container->log_source, QD_LOG_TRACE, "Closed sender link %s. n_senders: %d", pn_link_name(pn_link), qd_conn->n_senders); assert (qd_conn->n_senders >= 0); } } if (qd_link->close_sess_with_link && qd_link->pn_sess && pn_link_state(pn_link) == (PN_LOCAL_CLOSED | PN_REMOTE_CLOSED)) pn_session_close(qd_link->pn_sess); } } break; case PN_LINK_FLOW : pn_link = pn_event_link(event); qd_link = (qd_link_t*) pn_link_get_context(pn_link); if (qd_link && qd_link->node && qd_link->node->ntype->link_flow_handler) qd_link->node->ntype->link_flow_handler(qd_link->node->context, qd_link); break; case PN_DELIVERY : delivery = pn_event_delivery(event); if (pn_delivery_readable(delivery)) do_receive(delivery); if (pn_delivery_updated(delivery)) { do_updated(delivery); pn_delivery_clear(delivery); } break; case PN_EVENT_NONE : case PN_REACTOR_INIT : case PN_REACTOR_QUIESCED : case PN_REACTOR_FINAL : case PN_TIMER_TASK : case PN_CONNECTION_INIT : case PN_CONNECTION_BOUND : case PN_CONNECTION_UNBOUND : case PN_CONNECTION_LOCAL_OPEN : case PN_CONNECTION_LOCAL_CLOSE : case PN_CONNECTION_FINAL : case PN_SESSION_INIT : case PN_SESSION_LOCAL_OPEN : case PN_SESSION_LOCAL_CLOSE : case PN_SESSION_FINAL : case PN_LINK_INIT : case PN_LINK_LOCAL_OPEN : case PN_LINK_LOCAL_CLOSE : case PN_LINK_LOCAL_DETACH : case PN_LINK_FINAL : case PN_TRANSPORT : case PN_TRANSPORT_ERROR : case PN_TRANSPORT_HEAD_CLOSED : case PN_TRANSPORT_TAIL_CLOSED : case PN_TRANSPORT_CLOSED : case PN_TRANSPORT_AUTHENTICATED : case PN_SELECTABLE_INIT : case PN_SELECTABLE_UPDATED : case PN_SELECTABLE_READABLE : case PN_SELECTABLE_WRITABLE : case PN_SELECTABLE_ERROR : case PN_SELECTABLE_EXPIRED : case PN_SELECTABLE_FINAL : break; } return 1; }