/* Create and send error response */ static void send_reply_err(pj_turn_allocation *alloc, const pj_stun_rx_data *rdata, pj_bool_t cache, int code, const char *errmsg) { pj_status_t status; status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, NULL, cache, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr)); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error sending STUN error response", status); return; } }
static pj_status_t server_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); return pj_stun_session_respond(sess, rdata, 0, NULL, NULL, PJ_TRUE, src_addr, src_addr_len); }
/* * Create new allocation. */ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, const pj_sockaddr_t *src_addr, unsigned src_addr_len, const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc) { pj_turn_srv *srv = transport->listener->server; const pj_stun_msg *msg = rdata->msg; pj_pool_t *pool; alloc_request req; pj_turn_allocation *alloc; pj_stun_session_cb sess_cb; char str_tmp[80]; pj_status_t status; /* Parse ALLOCATE request */ status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL); /* Init allocation structure */ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation); alloc->pool = pool; alloc->obj_name = pool->obj_name; alloc->relay.tp.sock = PJ_INVALID_SOCKET; alloc->server = transport->listener->server; alloc->bandwidth = req.bandwidth; /* Set transport */ alloc->transport = transport; pj_turn_transport_add_ref(transport, alloc); alloc->hkey.tp_type = transport->listener->tp_type; pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len); status = pj_lock_create_recursive_mutex(pool, alloc->obj_name, &alloc->lock); if (status != PJ_SUCCESS) { goto on_error; } /* Create peer hash table */ alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Create channel hash table */ alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Print info */ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(transport->listener->tp_type)); alloc->info[3] = ':'; pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3); /* Create STUN session to handle STUN communication with client */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_msg = &stun_on_send_msg; sess_cb.on_rx_request = &stun_on_rx_request; sess_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, &sess_cb, PJ_FALSE, NULL, &alloc->sess); if (status != PJ_SUCCESS) { goto on_error; } /* Attach to STUN session */ pj_stun_session_set_user_data(alloc->sess, alloc); /* Init authentication credential */ status = init_cred(alloc, msg); if (status != PJ_SUCCESS) { goto on_error; } /* Attach authentication credential to STUN session */ pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM, &alloc->cred); /* Create the relay resource */ status = create_relay(srv, alloc, msg, &req, &alloc->relay); if (status != PJ_SUCCESS) { goto on_error; } /* Register this allocation */ pj_turn_srv_register_allocation(srv, alloc); /* Respond to ALLOCATE request */ status = send_allocate_response(alloc, srv_sess, transport, rdata); if (status != PJ_SUCCESS) goto on_error; /* Done */ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp, sizeof(str_tmp), 3); PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s", alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp)); /* Success */ *p_alloc = alloc; return PJ_SUCCESS; on_error: /* Send reply to the ALLOCATE request */ pj_strerror(status, str_tmp, sizeof(str_tmp)); pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp, transport, PJ_TRUE, src_addr, src_addr_len); /* Cleanup */ destroy_allocation(alloc); return status; }
/* Parse ALLOCATE request */ static pj_status_t parse_allocate_req(alloc_request *cfg, pj_stun_session *sess, const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *req = rdata->msg; pj_stun_bandwidth_attr *attr_bw; pj_stun_req_transport_attr *attr_req_tp; pj_stun_res_token_attr *attr_res_token; pj_stun_lifetime_attr *attr_lifetime; pj_bzero(cfg, sizeof(*cfg)); /* Get BANDWIDTH attribute, if any. */ attr_bw = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0); if (attr_bw) { cfg->bandwidth = attr_bw->value; } else { cfg->bandwidth = DEFA_CLIENT_BANDWIDTH; } /* Check if we can satisfy the bandwidth */ if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, "Invalid bandwidth", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED); } /* MUST have REQUESTED-TRANSPORT attribute */ attr_req_tp = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0); if (attr_req_tp == NULL) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "Missing REQUESTED-TRANSPORT attribute", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value); /* Can only support UDP for now */ if (cfg->tp_type != PJ_TURN_TP_UDP) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, NULL, NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO); } /* Get RESERVATION-TOKEN attribute, if any */ attr_res_token = (pj_stun_res_token_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN, 0); if (attr_res_token) { /* We don't support RESERVATION-TOKEN for now */ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "RESERVATION-TOKEN is not supported", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } /* Get LIFETIME attribute */ attr_lifetime = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0); if (attr_lifetime) { cfg->lifetime = attr_lifetime->value; if (cfg->lifetime < MIN_LIFETIME) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "LIFETIME too short", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } if (cfg->lifetime > MAX_LIFETIME) cfg->lifetime = MAX_LIFETIME; } else { cfg->lifetime = DEF_LIFETIME; } return PJ_SUCCESS; }