turn_usage.c

来自「一个开源的sip源代码」· C语言 代码 · 共 1,431 行 · 第 1/3 页

C
1,431
字号
    /* Process REQUESTED-IP attribute */
    if (a_rip && a_rip->sockaddr.addr.sa_family != PJ_AF_INET) {
	client_respond(client, msg, PJ_STUN_SC_INVALID_IP_ADDR, NULL,
		       src_addr, src_addr_len);
	return PJ_SUCCESS;
	
    } else if (a_rip) {
	req_addr.sin_addr.s_addr = a_rip->sockaddr.ipv4.sin_addr.s_addr;
    }

    /* Process REQUESTED-PORT-PROPS attribute */
    if (a_rpp) {
	unsigned port;

	rpp_bits = (a_rpp->value & 0x00030000) >> 16;
	port = (a_rpp->value & 0xFFFF);
	req_addr.sin_port = pj_htons((pj_uint8_t)port);
    } else {
	rpp_bits = 0;
    }

    /* Process LIFETIME attribute */
    if (a_lf && a_lf->value > client->tu->max_lifetime) {
	client->lifetime = client->tu->max_lifetime;
    } else if (a_lf) {
	client->lifetime = a_lf->value;
    } else {
	client->lifetime = client->tu->max_lifetime;
    }

    /* Allocate socket if we don't have one */
    if (client->key == NULL) {
	int err_code;

	PJ_LOG(4,(THIS_FILE, "TURN client %s: received initial Allocate "
			     "request, requested type:addr:port=%s:%s:%d, rpp "
			     "bits=%d, bw=%dkbps, lifetime=%d",
		  client->obj_name, get_tp_type(client->sock_type),
		  pj_inet_ntoa(req_addr.sin_addr), pj_ntohs(req_addr.sin_port),
		  rpp_bits, client->bw_kbps, client->lifetime));

	status = tu_alloc_port(client->tu, client->sock_type, rpp_bits, 
			       &req_addr, &client->sock, &err_code);
	if (status != PJ_SUCCESS) {
	    char errmsg[PJ_ERR_MSG_SIZE];

	    pj_strerror(status, errmsg, sizeof(errmsg));
	    PJ_LOG(4,(THIS_FILE, "TURN client %s: error allocating relay port"
				 ": %s",
		      client->obj_name, errmsg));

	    client_respond(client, msg, err_code, NULL,
			   src_addr, src_addr_len);

	    return status;
	}

	status = client_create_relay(client);
	if (status != PJ_SUCCESS) {
	    client_respond(client, msg, PJ_STUN_SC_SERVER_ERROR, NULL,
			   src_addr, src_addr_len);
	    return status;
	}
    } else {
	/* Otherwise check if the port parameter stays the same */
	/* TODO */
	PJ_LOG(4,(THIS_FILE, "TURN client %s: received Allocate refresh, "
			     "lifetime=%d",
		  client->obj_name, client->lifetime));
    }

    /* Refresh timer */
    if (client->expiry_timer.id != PJ_FALSE) {
	pj_timer_heap_cancel(client->tu->timer_heap, &client->expiry_timer);
	client->expiry_timer.id = PJ_FALSE;
    }
    timeout.sec = client->lifetime;
    timeout.msec = 0;
    pj_timer_heap_schedule(client->tu->timer_heap, &client->expiry_timer, &timeout);
    client->expiry_timer.id = PJ_TRUE;

    /* Done successfully, create and send success response */
    status = pj_stun_session_create_res(client->session, msg, 
					0, NULL, &response);
    if (status != PJ_SUCCESS) {
	return status;
    }

    pj_stun_msg_add_uint_attr(response->pool, response->msg, 
			      PJ_STUN_ATTR_BANDWIDTH, client->bw_kbps);
    pj_stun_msg_add_uint_attr(response->pool, response->msg,
			      PJ_STUN_ATTR_LIFETIME, client->lifetime);
    pj_stun_msg_add_sockaddr_attr(response->pool, response->msg,
				 PJ_STUN_ATTR_MAPPED_ADDR, PJ_FALSE,
				 src_addr, src_addr_len);
    pj_stun_msg_add_sockaddr_attr(response->pool, response->msg,
				 PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
				 src_addr, src_addr_len);

    addr_len = sizeof(req_addr);
    pj_sock_getsockname(client->sock, &req_addr, &addr_len);
    pj_stun_msg_add_sockaddr_attr(response->pool, response->msg,
				 PJ_STUN_ATTR_RELAY_ADDR, PJ_FALSE,
				 &client->alloc_addr, addr_len);

    PJ_LOG(4,(THIS_FILE, "TURN client %s: relay allocated or refreshed, "
			 "internal address is %s:%s:%d",
			 client->obj_name,
			 get_tp_type(client->sock_type),
			 pj_inet_ntoa(req_addr.sin_addr),
			 (int)pj_ntohs(req_addr.sin_port)));

    return pj_stun_session_send_msg(client->session, PJ_TRUE, 
				    src_addr, src_addr_len, response);
}


/*
 * Handle incoming Binding request.
 * This function is called by client_handle_stun_msg() below.
 */
static pj_status_t handle_binding_req(pj_stun_session *session,
				      const pj_stun_msg *msg,
				      const pj_sockaddr_t *src_addr,
				      unsigned src_addr_len)
{
    pj_stun_tx_data *tdata;
    pj_status_t status;

    /* Create response */
    status = pj_stun_session_create_res(session, msg, 0, NULL, 
					&tdata);
    if (status != PJ_SUCCESS)
	return status;

    /* Create MAPPED-ADDRESS attribute */
    pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
				 PJ_STUN_ATTR_MAPPED_ADDR,
				PJ_FALSE,
				src_addr, src_addr_len);

    /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */
    if (msg->hdr.magic == PJ_STUN_MAGIC) {
	status = 
	    pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
					 PJ_STUN_ATTR_XOR_MAPPED_ADDR,
					 PJ_TRUE,
					 src_addr, src_addr_len);
    }

    /* Send */
    status = pj_stun_session_send_msg(session, PJ_TRUE, 
				      src_addr, src_addr_len, tdata);
    return status;
}


/* 
 * client handling incoming STUN Set Active Destination request 
 * This function is called by client_handle_stun_msg() below.
 */
static pj_status_t client_handle_sad(struct turn_client *client,
				     const pj_stun_msg *msg,
				     const pj_sockaddr_t *src_addr,
				     unsigned src_addr_len)
{
    pj_stun_remote_addr_attr *a_raddr;

    a_raddr = (pj_stun_remote_addr_attr*)
	      pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REMOTE_ADDR, 0);
    if (!a_raddr) {
	/* Remote active destination needs to be cleared */
	client->active_peer = NULL;

    } else if (a_raddr->sockaddr.addr.sa_family != PJ_AF_INET) {
	/* Bad request (not IPv4) */
	client_respond(client, msg, PJ_STUN_SC_BAD_REQUEST, NULL,
		       src_addr, src_addr_len);
	return PJ_SUCCESS;

    } else if (client->active_peer) {
	/* Client tries to set new active destination without clearing
	 * it first. Reject with 439.
	 */
	client_respond(client, msg, PJ_STUN_SC_TRANSITIONING, NULL,
		       src_addr, src_addr_len);
	return PJ_SUCCESS;

    } else {
	struct peer *peer;
	pj_uint32_t hval = 0;

	/* Add a new peer/permission if we don't have one for this address */
	peer = client_get_peer(client, &a_raddr->sockaddr.ipv4, &hval);
	if (peer==NULL) {
	    peer = client_add_peer(client, &a_raddr->sockaddr.ipv4, hval);
	}

	/* Set active destination */
	client->active_peer = peer;
    }

    if (client->active_peer) {
	PJ_LOG(4,(THIS_FILE, 
		  "TURN client %s: active destination set to %s:%d",
		  client->obj_name,
		  pj_inet_ntoa(client->active_peer->addr.sin_addr),
		  (int)pj_ntohs(client->active_peer->addr.sin_port)));
    } else {
	PJ_LOG(4,(THIS_FILE, "TURN client %s: active destination cleared",
		  client->obj_name));
    }

    /* Respond with successful response */
    client_respond(client, msg, 0, NULL, src_addr, src_addr_len);

    return PJ_SUCCESS;
}


/* 
 * client handling incoming STUN Send Indication 
 * This function is called by client_handle_stun_msg() below.
 */
static pj_status_t client_handle_send_ind(struct turn_client *client,
					  const pj_stun_msg *msg)
{
    pj_stun_remote_addr_attr *a_raddr;
    pj_stun_data_attr *a_data;
    pj_uint32_t hval = 0;
    const pj_uint8_t *data;
    pj_ssize_t datalen;

    /* Get REMOTE-ADDRESS attribute */
    a_raddr = (pj_stun_remote_addr_attr*)
	      pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REMOTE_ADDR, 0);
    if (!a_raddr) {
	/* REMOTE-ADDRESS not present, discard packet */
	return PJ_SUCCESS;

    } else if (a_raddr->sockaddr.addr.sa_family != PJ_AF_INET) {
	/* REMOTE-ADDRESS present but not IPv4, discard packet */
	return PJ_SUCCESS;

    }

    /* Get the DATA attribute */
    a_data = (pj_stun_data_attr*)
	     pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
    if (a_data) {
	data = (const pj_uint8_t *)a_data->data;
	datalen = a_data->length;

    } else if (client->sock_type == PJ_SOCK_STREAM) {
	/* Discard if no Data and Allocation type is TCP */
	return PJ_SUCCESS;

    } else {
	data = (const pj_uint8_t *)"";
	datalen = 0;
    }

    /* Add to peer table if necessary */
    if (client_get_peer(client, &a_raddr->sockaddr.ipv4, &hval)==NULL)
	client_add_peer(client, &a_raddr->sockaddr.ipv4, hval);

    /* Send the packet */
    pj_ioqueue_sendto(client->key, &client->pkt_write_key, 
		      data, &datalen, 0,
		      &a_raddr->sockaddr.ipv4, sizeof(pj_sockaddr_in));

    return PJ_SUCCESS;
}


/* 
 * client handling unknown incoming STUN message.
 * This function is called by client_handle_stun_msg() below.
 */
static pj_status_t client_handle_unknown_msg(struct turn_client *client,
					     const pj_stun_msg *msg,
					     const pj_sockaddr_t *src_addr,
					     unsigned src_addr_len)
{
    PJ_LOG(4,(THIS_FILE, "TURN client %s: unhandled %s %s",
	      client->obj_name, pj_stun_get_method_name(msg->hdr.type),
	      pj_stun_get_class_name(msg->hdr.type)));

    if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
	return client_respond(client, msg, PJ_STUN_SC_BAD_REQUEST, NULL,
			      src_addr, src_addr_len);
    } else {
	/* Ignore */
	return PJ_SUCCESS;
    }
}


/* 
 * Main entry for handling STUN messages arriving on the main TURN port, 
 * for this client 
 */
static pj_status_t client_handle_stun_msg(struct turn_client *client,
					  const pj_stun_msg *msg,
					  const pj_sockaddr_t *src_addr,
					  unsigned src_addr_len)
{
    pj_status_t status;

    switch (msg->hdr.type) {
    case PJ_STUN_SEND_INDICATION:
	status = client_handle_send_ind(client, msg);
	break;

    case PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST:
	status = client_handle_sad(client, msg,
				   src_addr, src_addr_len);
	break;

    case PJ_STUN_ALLOCATE_REQUEST:
	status = client_handle_allocate_req(client, msg,
					    src_addr, src_addr_len);
	break;

    case PJ_STUN_BINDING_REQUEST:
	status = handle_binding_req(client->session, msg,
				    src_addr, src_addr_len);
	break;

    default:
	status = client_handle_unknown_msg(client, msg,
					   src_addr, src_addr_len);
	break;
    }

    return status;
}


PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos)
{
    return (pdu[pos+0] << 24) +
	   (pdu[pos+1] << 16) +
	   (pdu[pos+2] <<  8) +
	   (pdu[pos+3]);
}


/* 
 * Handle incoming data from peer 
 * This function is called by client_on_read_complete() below.
 */
static void client_handle_peer_data(struct turn_client *client,
				    unsigned bytes_read)
{
    struct peer *peer;
    pj_bool_t has_magic_cookie;
    pj_status_t status;

    /* Has the sender been registered as peer? */
    peer = client_get_peer(client, &client->pkt_src_addr, NULL);
    if (peer == NULL) {
	/* Nope. Discard packet */
	PJ_LOG(5,(THIS_FILE, 
		 "TURN client %s: discarded data from %s:%d",
		 client->obj_name,
		 pj_inet_ntoa(client->pkt_src_addr.sin_addr),
		 (int)pj_ntohs(client->pkt_src_addr.sin_port)));
	return;
    }

    /* Check if packet has STUN magic cookie */
    has_magic_cookie = (GET_VAL32(client->pkt, 4) == PJ_STUN_MAGIC);

    /* If this is the Active Destination and the packet doesn't have
     * STUN magic cookie, send the packet to client as is.
     */
    if (peer == client->active_peer && !has_magic_cookie) {
	pj_stun_usage_sendto(client->tu->usage, client->pkt, bytes_read, 0,
			     &client->client_src_addr, sizeof(pj_sockaddr_in));
    } else {
	/* Otherwise wrap in Data Indication */
	pj_stun_tx_data *data_ind;

	status = pj_stun_session_create_ind(client->session, 
					    PJ_STUN_DATA_INDICATION,
					    &data_ind);
	if (status != PJ_SUCCESS)
	    return;

	pj_stun_msg_add_sockaddr_attr(data_ind->pool, data_ind->msg, 
				      PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
				      &client->pkt_src_addr,
				      client->pkt_src_addr_len);
	pj_stun_msg_add_binary_attr(data_ind->pool, data_ind->msg,
				    PJ_STUN_ATTR_DATA, 
				    client->pkt, bytes_read);


	pj_stun_session_send_msg(client->session, PJ_FALSE,
				 &client->client_src_addr, 
				 sizeof(pj_sockaddr_in),
				 data_ind);
    }
}


/* 
 * This callback is called by the ioqueue when read operation has
 * completed on the allocated relay port.
 */
static void client_on_read_complete(pj_ioqueue_key_t *key, 
				    pj_ioqueue_op_key_t *op_key, 
				    pj_ssize_t bytes_read)
{
    enum { MAX_LOOP = 10 };
    struct turn_client *client;
    unsigned count;
    pj_status_t status;

    PJ_UNUSED_ARG(op_key);

    client = (struct turn_client*) pj_ioqueue_get_user_data(key);
    
    /* Lock client */
    pj_mutex_lock(client->mutex);

    for (count=0; ; ++count) {
	unsigned flags;

	if (bytes_read > 0) {
	    /* Received data from peer! */
	    client_handle_peer_data(client, bytes_read);

	} else if (bytes_read < 0) {
	    char errmsg[PJ_ERR_MSG_SIZE];
	    pj_strerror(-bytes_read, errmsg, sizeof(errmsg));
	    PJ_LOG(4,(THIS_FILE, "TURN client %s: error reading data "
				 "from allocated relay port: %s",
				 client->obj_name, errmsg));
	}

	bytes_read = sizeof(client->pkt);
	flags = (count >= MAX_LOOP) ? PJ_IOQUEUE_ALWAYS_ASYNC : 0;
	client->pkt_src_addr_len = sizeof(client->pkt_src_addr);
	status = pj_ioqueue_recvfrom(client->key, 
				     &client->pkt_read_key,
				     client->pkt, &bytes_read, flags,
				     &client->pkt_src_addr, 
				     &client->pkt_src_addr_len);
	if (status == PJ_EPENDING)
	    break;
    }

    /* Unlock client */
    pj_mutex_unlock(client->mutex);
}


/* On Allocation timer timeout (i.e. we don't receive new Allocate request
 * to refresh the allocation in time)
 */
static void client_on_expired(pj_timer_heap_t *th, pj_timer_entry *e)
{
    struct turn_client *client;

    PJ_UNUSED_ARG(th);

    client = (struct turn_client*) e->user_data;

    PJ_LOG(4,(THIS_FILE, "TURN client %s: allocation timer timeout, "
			 "destroying client",
			 client->obj_name));
    client_destroy(client, PJ_SUCCESS);
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?