Ejemplo n.º 1
0
static void send_tcp_ping(t_event *event) {
	string log_msg;
	
	t_event_tcp_ping *e = dynamic_cast<t_event_tcp_ping *>(event);
	assert(e);
	
	t_connection *conn = connection_table->get_connection(
			e->get_dst_addr(), e->get_dst_port());
			
	if (!conn) {
		// There is no connection to send the ping.
		log_msg = "Connection to ";
		log_msg += h_ip2str(e->get_dst_addr());
		log_msg += ":";
		log_msg += int2str(e->get_dst_port());
		log_msg += " is gone.";
		log_file->write_report(log_msg, "::send_tcp_ping", LOG_SIP, LOG_WARNING);
					
		// Signal the transaction layer that the connection is gone.
		evq_trans_layer->push_broken_connection(e->get_user_uri());
		
		return;
	}
	
	conn->async_send(TCP_PING_PACKET, strlen(TCP_PING_PACKET));
	
	connection_table->unlock();
}
Ejemplo n.º 2
0
// Create a request within a dialog
// RFC 3261 12.2.1.1
t_request *t_abstract_dialog::create_request(t_method m) {
	t_user *user_config = phone_user->get_user_profile();
	
	t_request *r = new t_request(m);
	MEMMAN_NEW(r);

	// To header
	r->hdr_to.set_uri(remote_uri);
	r->hdr_to.set_display(remote_display);
	r->hdr_to.set_tag(remote_tag);

	// From header
	r->hdr_from.set_uri(local_uri);
	r->hdr_from.set_display(local_display);
	r->hdr_from.set_tag(local_tag);

	// Call-ID header
	r->hdr_call_id.set_call_id(call_id);

	// CSeq header
	r->hdr_cseq.set_method(m);
	r->hdr_cseq.set_seqnr(++local_seqnr);

	// Set Max-Forwards header
	r->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);

	// User-Agent
	SET_HDR_USER_AGENT(r->hdr_user_agent);

	// RFC 3261 12.2.1.1
	// Request URI and Route header
	r->set_route(remote_target_uri, route_set);
        
        // Caculate destination set. A DNS request can result in multiple
        // IP address. In failover scenario's the request must be sent to
        // the next IP address in the list. As the request will be copied
        // in various places, the destination set must be calculated now.
        // In previous version the DNS request was done by the transaction
        // manager. This is too late as the transaction manager gets a copy
        // of the request. The destination set should be set in the copy
        // kept by the dialog.
        r->calc_destinations(*user_config);
        
        // The Via header can only be created after the destinations
        // are calculated, because the destination deterimines which
        // local IP address should be used.
        
        // Via header
        unsigned long local_ip = r->get_local_ip();
	t_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));
	r->hdr_via.add_via(via);

	return r;
}
Ejemplo n.º 3
0
static void send_sip_udp(t_event *event) {
	t_event_network	*e;
	
	e = (t_event_network *)event;
	
	assert(e->dst_addr != 0);
	assert(e->dst_port != 0);

	// Set correct transport in topmost Via header of a request.
	// For a response the Via header is copied from the incoming request.
	t_sip_message *sip_msg = e->get_msg();
	if (sip_msg->get_type() == MSG_REQUEST) {
		sip_msg->hdr_via.via_list.front().transport = "UDP";
	}
	
	string m = sip_msg->encode();
	log_file->write_header("::send_sip_udp", LOG_SIP);
	log_file->write_raw("Send to: udp:");
	log_file->write_raw(h_ip2str(e->dst_addr));
	log_file->write_raw(":");
	log_file->write_raw(e->dst_port);
	log_file->write_endl();
	log_file->write_raw(m);
	log_file->write_endl();
	log_file->write_footer();
		
	bool msg_sent = false;
	int transmit_count = 0;
	while (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {
		try {
			sip_socket->sendto(e->dst_addr, e->dst_port, m.c_str(), m.size());
			num_non_icmp_errors = 0;
			msg_sent = true;
		} catch (int err) {
			if (!handle_socket_err(err, e->dst_addr, e->dst_port)) {
				// Discard packet.
				msg_sent = true;
			} else {
				if (transmit_count <= MAX_TRANSMIT_RETRIES) {
					// Sleep 100 ms
					struct timespec sleeptimer;
					sleeptimer.tv_sec = 0;
					sleeptimer.tv_nsec = 100000000;
					nanosleep(&sleeptimer, NULL);
				}
			}
		}
	}
}
Ejemplo n.º 4
0
void t_abstract_dialog::resend_request(t_client_request *cr) {
	t_user *user_config = phone_user->get_user_profile();
	t_request *req = cr->get_request();

	// A new sequence number must be assigned
	req->hdr_cseq.set_seqnr(++local_seqnr);

	// Create a new via-header. Otherwise the
	// request will be seen as a retransmission
	unsigned long local_ip = req->get_local_ip();
	req->hdr_via.via_list.clear();
	t_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));
	req->hdr_via.add_via(via);

	cr->renew(0);
	send_request(req, cr->get_tuid());
}
Ejemplo n.º 5
0
static void send_stun(t_event *event) {
	t_event_stun_request	*e;
	
	e = (t_event_stun_request *)event;
	
	assert(e->dst_addr != 0);
	assert(e->dst_port != 0);
	
	log_file->write_header("::send_stun", LOG_STUN);
	log_file->write_raw("Send to: ");
	log_file->write_raw(h_ip2str(e->dst_addr));
	log_file->write_raw(":");
	log_file->write_raw(e->dst_port);
	log_file->write_endl();
	log_file->write_raw(stunMsg2Str(*e->get_msg()));
	log_file->write_footer();
	
	StunAtrString stun_pass;
	stun_pass.sizeValue = 0;
	char m[STUN_MAX_MESSAGE_SIZE];
	int msg_size = stunEncodeMessage(*e->get_msg(), m, 
		STUN_MAX_MESSAGE_SIZE, stun_pass, false);

	bool msg_sent = false;
	int transmit_count = 0;
	while (!msg_sent && transmit_count++ <= MAX_TRANSMIT_RETRIES) {	
		try {
			sip_socket->sendto(e->dst_addr, e->dst_port, m, msg_size);
			num_non_icmp_errors = 0;
			msg_sent = true;
		} catch (int err) {
			if (!handle_socket_err(err, e->dst_addr, e->dst_port)) {
				// Discard packet.
				msg_sent = true;
			} else {
				if (transmit_count <= MAX_TRANSMIT_RETRIES) {
					// Sleep 100 ms
					struct timespec sleeptimer;
					sleeptimer.tv_sec = 0;
					sleeptimer.tv_nsec = 100000000;
					nanosleep(&sleeptimer, NULL);
				}
			}
		}
	}
}
Ejemplo n.º 6
0
void t_phone_user::resend_request(t_request *req, bool is_register, t_client_request *cr) {
	// A new sequence number must be assigned
	if (is_register) {
		req->hdr_cseq.set_seqnr(++register_seqnr);
	} else {
		req->hdr_cseq.seqnr++;
	}

	// Create a new via-header. Otherwise the
	// request will be seen as a retransmission
	unsigned long local_ip = req->get_local_ip();
	req->hdr_via.via_list.clear();
	t_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));
	req->hdr_via.add_via(via);

	cr->renew(0);
	phone->send_request(user_config, req, cr->get_tuid());
}
Ejemplo n.º 7
0
t_request *t_phone_user::create_request(t_method m, const t_url &request_uri) const {
	t_request *req = new t_request(m);
	MEMMAN_NEW(req);

	// From
	req->hdr_from.set_uri(user_config->create_user_uri(false));
	req->hdr_from.set_display(user_config->get_display(false));
	req->hdr_from.set_tag(NEW_TAG);

	// Max-Forwards header (mandatory)
	req->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);

	// User-Agent
	SET_HDR_USER_AGENT(req->hdr_user_agent);
	
	// Set request URI and calculate destinations. By calculating
	// destinations now, the request can be resend to a next destination
	// if failover is needed.
	if (m == REGISTER) {
		// For a REGISTER do not use the service route for routing.
		req->uri = request_uri;
	} else {
		// RFC 3608
		// For all other requests, use the service route set for routing.
		req->set_route(request_uri, service_route);
	}

	req->calc_destinations(*user_config);
	
        // The Via header can only be created after the destinations
        // are calculated, because the destination deterimines which
        // local IP address should be used.
	
	// Via
	unsigned long local_ip = req->get_local_ip();
	t_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));
	req->hdr_via.add_via(via);

	return req;
}
Ejemplo n.º 8
0
t_request *t_subscription_dialog::create_request(t_method m) {
	t_user *user_config = phone_user->get_user_profile();
	t_request *r = t_abstract_dialog::create_request(m);
	
	// Contact header
	t_contact_param contact;
	switch (m) {
	case REFER:
	case SUBSCRIBE:
	case NOTIFY:
		// RFC 3265 7.1, RFC 3515 2.2
		// Contact header is mandatory
		contact.uri.set_url(user_config->create_user_contact(false,
				h_ip2str(r->get_local_ip())));
		r->hdr_contact.add_contact(contact);
		break;
	default:
		break;
	}
	
	return r;
}
Ejemplo n.º 9
0
bool get_stun_binding(t_user *user_config, unsigned short src_port, unsigned long &mapped_ip,
	unsigned short &mapped_port, int &err_code, string &err_reason)
{
	list<t_ip_port> destinations = 
		user_config->get_stun_server().get_h_ip_srv("udp");
	
	if (destinations.empty()) {
		// Cannot resolve STUN server address.
		log_file->write_header("::get_stun_binding", LOG_NORMAL, LOG_CRITICAL);
		log_file->write_raw("Failed to resolve: ");
		log_file->write_raw(user_config->get_stun_server().encode());
		log_file->write_endl();
		log_file->write_raw("Return internal STUN bind error: 404 Not Found");
		log_file->write_endl();
		log_file->write_footer();
		
		err_code = 404;
		err_reason = "Not Found";
		return false;
	}
	
	int num_transmissions = 0;
	int wait_intval = DUR_STUN_START_INTVAL;

	t_socket_udp sock(src_port);
	sock.connect(destinations.front().ipaddr, destinations.front().port);
		
	// Build STUN request
	char buf[STUN_MAX_MESSAGE_SIZE + 1];
	StunMessage req_bind;
	StunAtrString stun_null_str;
	stun_null_str.sizeValue = 0;	
	stunBuildReqSimple(&req_bind, stun_null_str, false, false);	
	char req_msg[STUN_MAX_MESSAGE_SIZE];
	int req_msg_size = stunEncodeMessage(req_bind, req_msg, 
		STUN_MAX_MESSAGE_SIZE, stun_null_str, false);
		
	// Send STUN request and retransmit till a response is received.
	while (num_transmissions < STUN_MAX_TRANSMISSIONS) {
		bool ret;

		try {
			sock.send(req_msg, req_msg_size);
		}
		catch (int err) {
			// Socket error (probably ICMP error)
			// Failover to next destination
			log_file->write_report("Send failed. Failover to next destination.",
					"::get_stun_binding");	
						
			destinations.pop_front();
			if (destinations.empty()) {
				log_file->write_report("No next destination for failover.",
					"::get_stun_binding");
				break;
			}
			
			num_transmissions = 0;
			wait_intval = DUR_STUN_START_INTVAL;
			sock.connect(destinations.front().ipaddr, destinations.front().port);
			continue;
		}
		
		log_file->write_header("::get_stun_binding", LOG_STUN);
		log_file->write_raw("Send to: ");
		log_file->write_raw(h_ip2str(destinations.front().ipaddr));
		log_file->write_raw(":");
		log_file->write_raw(destinations.front().port);
		log_file->write_endl();
		log_file->write_raw(stunMsg2Str(req_bind));
		log_file->write_footer();
			
		try {
			ret = sock.select_read(wait_intval);
		}
		catch (int err) {
			// Socket error (probably ICMP error)
			// Failover to next destination
			log_file->write_report("Select failed. Failover to next destination.",
					"::get_stun_binding");	
						
			destinations.pop_front();
			if (destinations.empty()) {
				log_file->write_report("No next destination for failover.",
					"::get_stun_binding");
				break;
			}
			
			num_transmissions = 0;
			wait_intval = DUR_STUN_START_INTVAL;
			sock.connect(destinations.front().ipaddr, destinations.front().port);
			continue;
		}
			
		if (!ret) {
			// Time out
			num_transmissions++;
			if (wait_intval < DUR_STUN_MAX_INTVAL) {
				wait_intval *= 2;
			}
			continue;
		}
			
		// A message has been received
		int resp_msg_size;
		try {
			resp_msg_size = sock.recv(buf, STUN_MAX_MESSAGE_SIZE + 1);
		}
		catch (int err) {
			// Socket error (probably ICMP error)
			// Failover to next destination
			log_file->write_report("Recv failed. Failover to next destination.",
					"::get_stun_binding");	
						
			destinations.pop_front();
			if (destinations.empty()) {
				log_file->write_report("No next destination for failover.",
					"::get_stun_binding");
				break;
			}
			
			num_transmissions = 0;
			wait_intval = DUR_STUN_START_INTVAL;
			sock.connect(destinations.front().ipaddr, destinations.front().port);
			continue;
		}
			
		StunMessage resp_bind;
		
		if (!stunParseMessage(buf, resp_msg_size, resp_bind, false)) {
			log_file->write_report(
				"Received faulty STUN message", "::get_stun_binding", 
					LOG_STUN);
			num_transmissions++;
			if (wait_intval < DUR_STUN_MAX_INTVAL) {
				wait_intval *= 2;
			}
			continue;
		}
		
		log_file->write_header("::get_stun_binding", LOG_STUN);
		log_file->write_raw("Received from: ");
		log_file->write_raw(h_ip2str(destinations.front().ipaddr));
		log_file->write_raw(":");
		log_file->write_raw(destinations.front().port);
		log_file->write_endl();
		log_file->write_raw(stunMsg2Str(resp_bind));
		log_file->write_footer();
		
		// Check if id in msgHdr matches
		if (!stunEqualId(resp_bind, req_bind)) {
			num_transmissions++;
			if (wait_intval < DUR_STUN_MAX_INTVAL) {
				wait_intval *= 2;
			}
			continue;
		}
				
		if (resp_bind.msgHdr.msgType == BindResponseMsg && 
		    resp_bind.hasMappedAddress) {
		    	// Bind response received
			mapped_ip = resp_bind.mappedAddress.ipv4.addr;
			mapped_port = resp_bind.mappedAddress.ipv4.port;
			return true;
		}
			
		if (resp_bind.msgHdr.msgType == BindErrorResponseMsg &&
		    resp_bind.hasErrorCode) 
		{
			// Bind error received
			err_code = resp_bind.errorCode.errorClass * 100 +
				   resp_bind.errorCode.number;
			char s[STUN_MAX_STRING + 1];
			strncpy(s, resp_bind.errorCode.reason, STUN_MAX_STRING);
			s[STUN_MAX_STRING] = 0;
			err_reason = s;
			return false;
		}
			
		// A wrong response has been received.
		log_file->write_report(
			"Invalid STUN response received", "::get_stun_binding", 
				LOG_NORMAL);

		err_code = 500;
		err_reason = "Server Error";
		return false;
	}
		
	// Request timed out
	log_file->write_report("STUN request timeout", "::get_stun_binding", 
			LOG_NORMAL);
				
	err_code = 408;
	err_reason = "Request Timeout";
	return false;
}
Ejemplo n.º 10
0
void t_phone_user::registration(t_register_type register_type, bool re_register,
		unsigned long expires)
{
	// If STUN is enabled, then do a STUN query before registering to
	// determine the public IP address.
	if (register_type == REG_REGISTER && use_stun) {
		if (stun_public_ip_sip == 0) {
			send_stun_request();
			register_after_stun = true;
			registration_time = expires;
			return;
		}
		
		stun_binding_inuse_registration = true;
	}

	// Stop registration timer for non-query request
	if (register_type != REG_QUERY) {
		phone->stop_timer(PTMR_REGISTRATION, this);
	}

	// Create call-id if no call-id is created yet
	if (register_call_id == "") {
		register_call_id = NEW_CALL_ID(user_config);
	}

	// RFC 3261 10.2
	// Construct REGISTER request

	t_request *req = create_request(REGISTER, 
			t_url(string(USER_SCHEME) + ":" + user_config->get_domain()));

	// To
	req->hdr_to.set_uri(user_config->create_user_uri(false));
	req->hdr_to.set_display(user_config->get_display(false));

	//Call-ID
	req->hdr_call_id.set_call_id(register_call_id);

	// CSeq
	req->hdr_cseq.set_method(REGISTER);
	req->hdr_cseq.set_seqnr(++register_seqnr);

	// Contact
        t_contact_param contact;

        switch (register_type) {
        case REG_REGISTER:
        	// URI
                contact.uri.set_url(user_config->create_user_contact(false,
                		h_ip2str(req->get_local_ip())));
                
                // Expires
                if (expires > 0) {
			if (user_config->get_registration_time_in_contact()) {
				contact.set_expires(expires);
			} else {
				req->hdr_expires.set_time(expires);
			}
		}
		
		// q-value
		if (user_config->get_reg_add_qvalue()) {
			contact.set_qvalue(user_config->get_reg_qvalue());
		}

                req->hdr_contact.add_contact(contact);
                break;
        case REG_DEREGISTER:
                contact.uri.set_url(user_config->create_user_contact(false,
                		h_ip2str(req->get_local_ip())));
 		if (user_config->get_registration_time_in_contact()) {
			contact.set_expires(0);
		} else {
			req->hdr_expires.set_time(0);
		}
                req->hdr_contact.add_contact(contact);
                break;
        case REG_DEREGISTER_ALL:
                req->hdr_contact.set_any();
                req->hdr_expires.set_time(0);
                break;
        default:
                break;
        }

	// Allow
	SET_HDR_ALLOW(req->hdr_allow, user_config);

	// Store request in the proper place
	t_tuid tuid;

        switch(register_type) {
        case REG_REGISTER:
		// Delete a possible pending registration request
		if (r_register) {
			MEMMAN_DELETE(r_register);
			delete r_register;
		}
                r_register = new t_client_request(user_config, req, 0);
		MEMMAN_NEW(r_register);
                tuid = r_register->get_tuid();

                // Store expiration time for re-registration.
                registration_time = expires;
                break;
        case REG_QUERY:
		// Delete a possible pending query registration request
		if (r_query_register) {
			MEMMAN_DELETE(r_query_register);
			delete r_query_register;
		}
                r_query_register = new t_client_request(user_config, req, 0);
		MEMMAN_NEW(r_query_register);
                tuid = r_query_register->get_tuid();
                break;
        case REG_DEREGISTER:
        case REG_DEREGISTER_ALL:
		// Delete a possible pending de-registration request
		if (r_deregister) {
			MEMMAN_DELETE(r_deregister);
			delete r_deregister;
		}
                r_deregister = new t_client_request(user_config, req, 0);
		MEMMAN_NEW(r_deregister);
                tuid = r_deregister->get_tuid();
                break;
        default:
                assert(false);
        }

        // Send REGISTER
        authorizor.set_re_register(re_register);
	ui->cb_register_inprog(user_config, register_type);
        phone->send_request(user_config, req, tuid);
	MEMMAN_DELETE(req);
        delete req;
}
Ejemplo n.º 11
0
string t_phone_user::get_ip_sip(const string &auto_ip) const {
	if (stun_public_ip_sip) return h_ip2str(stun_public_ip_sip);
	if (user_config->get_use_nat_public_ip()) return user_config->get_nat_public_ip();
	if (LOCAL_IP == AUTO_IP4_ADDRESS) return auto_ip;
	return LOCAL_IP;
}
Ejemplo n.º 12
0
// Check if the error is caused by an incoming ICMP error. If so, then deliver
// the ICMP error to the transaction manager.
//
// err - error returned by sendto
// dst_addr - destination IP address of packet that failed to be sent
// dst_port - destination port of packet that failed to be sent
//
// Returns true if the packet that failed to be sent, should still be sent.
// Returns false if the packet that failed to be sent, should be discarded.
static bool handle_socket_err(int err, unsigned long dst_addr, unsigned short dst_port) {
	string log_msg;

	// Check if an ICMP error has been received
	t_icmp_msg icmp;
	if (sip_socket->get_icmp(icmp)) {
		log_msg = "Received ICMP from: ";
		log_msg += h_ip2str(icmp.icmp_src_ipaddr);
		log_msg += "\nICMP type: ";
		log_msg += int2str(icmp.type);
		log_msg += "\nICMP code: ";
		log_msg += int2str(icmp.code);
		log_msg += "\nDestination of packet causing ICMP: ";
		log_msg += h_ip2str(icmp.ipaddr);
		log_msg += ":";
		log_msg += int2str(icmp.port);
		log_msg += "\nSocket error: ";
		log_msg += int2str(err);
		log_msg += " ";
		log_msg += get_error_str(err);
		log_file->write_report(log_msg, "::hanlde_socket_err", LOG_NORMAL);

		evq_trans_mgr->push_icmp(icmp);
		
		num_non_icmp_errors = 0;
		
		// If the ICMP error comes from the same destination as the
		// destination of the packet that failed to be sent, then the
		// packet should be discarded as it can most likely not be
		// delivered and would cause an infinite loop of ICMP errors
		// otherwise.
		if (icmp.ipaddr == dst_addr && icmp.port == dst_port) {
			return false;
		}
	} else {
		// Even if an ICMP message is received this code can get executed.
		// Sometimes the error is already present on the socket, but the ICMP
		// message is not yet queued.
		log_msg = "Failed to send to SIP UDP socket.\n";
		log_msg += "Error code: ";
		log_msg += int2str(err);
		log_msg += "\n";
		log_msg += get_error_str(err);
		log_file->write_report(log_msg, "::handle_socket_err");
		
		num_non_icmp_errors++;
		
		/*
		 * non-ICMP errors occur when a destination on the same
		 * subnet cannot be reached. So this code seems to be
		 * harmful.
		if (num_non_icmp_errors > 100) {
			log_msg = "Excessive number of socket errors.";
			log_file->write_report(log_msg, "::handle_socket_err", 
				LOG_NORMAL, LOG_CRITICAL);
			log_msg = TRANSLATE("Excessive number of socket errors.");
			ui->cb_show_msg(log_msg, MSG_CRITICAL);
			exit(1);
		}
		*/
	}
	
	return true;
}
Ejemplo n.º 13
0
void *sender_loop(void *arg) {
	t_event 	*event;
	t_event_network	*ev_network;
	unsigned long	local_ipaddr;

	bool quit = false;
	while (!quit) {
		event = evq_sender->pop();
		
		switch(event->get_type()) {
		case EV_NETWORK:
			ev_network = dynamic_cast<t_event_network *>(event);
			local_ipaddr = get_src_ip4_address_for_dst(ev_network->dst_addr);
			
			if (local_ipaddr == 0) {
				log_file->write_header("::sender_loop", LOG_NORMAL, LOG_CRITICAL);
				log_file->write_raw("Cannot get source IP address for destination: ");
				log_file->write_raw(h_ip2str(ev_network->dst_addr));
				log_file->write_endl();
				log_file->write_footer();
				
				evq_trans_mgr->push_failure(FAIL_TRANSPORT,
					ev_network->get_msg()->hdr_via.via_list.front().branch,
					ev_network->get_msg()->hdr_cseq.method);
				break;
			}
			
			if (!ev_network->get_msg()->local_ip_check()) {
				log_file->write_report("Local IP check failed",
					"::sender_loop", LOG_NORMAL, LOG_CRITICAL);
				break;
			}
			
			if (ev_network->transport == "udp") {
				send_sip_udp(event);
			} else if (ev_network->transport == "tcp") {
				send_sip_tcp(event);
			} else {
				log_file->write_header("::sender_loop", LOG_NORMAL, LOG_WARNING);
				log_file->write_raw("Received unsupported transport: ");
				log_file->write_raw(ev_network->transport);
				log_file->write_endl();
				log_file->write_footer();
			}
			break;
		case EV_STUN_REQUEST:
			send_stun(event);
			break;
		case EV_NAT_KEEPALIVE:
			send_nat_keepalive(event);
			break;
		case EV_TCP_PING:
			send_tcp_ping(event);
			break;
		case EV_QUIT:
			quit = true;
			break;
		default:
			assert(false);
		}

		MEMMAN_DELETE(event);
		delete event;
	}
	
	return NULL;
}
Ejemplo n.º 14
0
void *tcp_sender_loop(void *arg) {
	string log_msg;
	list<t_connection *> writable_connections;
	
	while(true) {
		writable_connections.clear();
		writable_connections = connection_table->select_write(NULL);
		
		if (writable_connections.empty()) {
			// Another thread cancelled the select command.
			// Stop listening.
			break;
		}
		
		// NOTE: The connection table is now locked.
		
		for (list<t_connection *>::iterator it = writable_connections.begin();
			it != writable_connections.end(); ++it)
		{
			try {
				(*it)->write();
			} catch (int err) {
				if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) {
					continue;
				}
				
				unsigned long remote_addr;
				unsigned short remote_port;
			
				(*it)->get_remote_address(remote_addr, remote_port);
				
				log_msg = "Got error on socket to ";
				log_msg += h_ip2str(remote_addr);
				log_msg += ":";
				log_msg += int2str(remote_port);
				log_msg += " - ";
				log_msg += get_error_str(err);
				log_file->write_report(log_msg, "::tcp_sender_loop", LOG_SIP, LOG_WARNING);
				
				// Connection is broken. 
				// Signal the transaction layer that the connection is broken for
				// all associated registered URI's.
				const list<t_url> &uris = (*it)->get_registered_uri_set();
				for (list<t_url>::const_iterator it_uri = uris.begin(); 
				     it_uri != uris.end(); ++it_uri)
				{
					evq_trans_layer->push_broken_connection(*it_uri);
				}
				
				// Remove the broken connection.
				connection_table->remove_connection(*it);
				MEMMAN_DELETE(*it);
				delete *it;
				
				continue;
			}
		}
		
		connection_table->unlock();
	}
	
	log_file->write_report("TCP sender terminated.", "::tcp_sender_loop");
	return NULL;
}
Ejemplo n.º 15
0
static void send_sip_tcp(t_event *event) {
	t_event_network	*e;
	bool new_connection = false;
	
	e = (t_event_network *)event;
	unsigned long dst_addr = e->dst_addr;
	unsigned short dst_port = e->dst_port;
	
	assert(dst_addr != 0);
	assert(dst_port != 0);
	
	// Set correct transport in topmost Via header of a request.
	// For a response the Via header is copied from the incoming request.
	t_sip_message *sip_msg = e->get_msg();
	if (sip_msg->get_type() == MSG_REQUEST) {
		sip_msg->hdr_via.via_list.front().transport = "TCP";
	}
	
	t_connection *conn = NULL;
	
	// If a connection exists then re-use this connection. Otherwise a new connection
	// must be opened.
	// For a request a connection to the destination address and port of the event
	// must be opened.
	// For a response a connection to the sent-by address and port in the Via header
	// must be opened.
	
	if (sip_msg->get_encoded_size() <= MAX_REUSE_CONN_SIZE) {
		// Re-use a connection only for small messages. Large messages cause
		// head of line blocking.
		conn = connection_table->get_connection(dst_addr, dst_port);
	} else {
		log_file->write_report(
			"Open new connection for large message.",
			"::send_sip_tcp", LOG_SIP, LOG_DEBUG);
	}
	
	if (!conn) {
		if (sip_msg->get_type() == MSG_RESPONSE) {
			t_ip_port dst_ip_port;
			sip_msg->hdr_via.get_response_dst(dst_ip_port);
			dst_addr = dst_ip_port.ipaddr;
			dst_port = dst_ip_port.port;
		}
		
		t_socket_tcp *tcp = new t_socket_tcp();
		MEMMAN_NEW(tcp);
		
		log_file->write_header("::send_sip_tcp", LOG_SIP, LOG_DEBUG);
		log_file->write_raw("Open connection to ");
		log_file->write_raw(h_ip2str(dst_addr));
		log_file->write_raw(":");
		log_file->write_raw(dst_port);
		log_file->write_endl();
		log_file->write_footer();
		
		try {
			tcp->connect(dst_addr, dst_port);
		} catch (int err) {
			evq_trans_mgr->push_failure(FAIL_TRANSPORT,
				sip_msg->hdr_via.via_list.front().branch,
				sip_msg->hdr_cseq.method);
			
			log_file->write_header("::send_sip_tcp", LOG_SIP, LOG_WARNING);
			log_file->write_raw("Failed to open connection to ");
			log_file->write_raw(h_ip2str(dst_addr));
			log_file->write_raw(":");
			log_file->write_raw(dst_port);
			log_file->write_endl();
			log_file->write_footer();
			
			delete tcp;
			MEMMAN_DELETE(tcp);
			
			return;
		}
		
		conn = new t_connection(tcp);
		MEMMAN_NEW(conn);
		
		// For large messages always a new connection is established.
		// No other messages should be sent via this connection to avoid
		// head of line blocking.
		if (sip_msg->get_encoded_size() > MAX_REUSE_CONN_SIZE) {
			conn->set_reuse(false);
		}
		
		new_connection = true;
	}
		
	// NOTE: if an existing connection was found, the connection table is now locked.
	
	// If persistent TCP connections are required, then
	// 1) If the SIP message is a registration request, then add the URI from the To-header 
	//    to the registered URI set of the connection.
	// 2) If the SIP message is a de-registration request then remove the URI from the
	//    To-header from the registered URI set of the connection.
	if (sip_msg->get_type() == MSG_REQUEST) {
		t_request *req = dynamic_cast<t_request *>(sip_msg);
		if (req->method == REGISTER) 
		{
			t_phone_user *pu = phone->find_phone_user(req->hdr_to.uri);
			if (pu) {
				t_user *user_config = pu->get_user_profile();
				assert(user_config);
				
				if (user_config->get_persistent_tcp()) {
					conn->update_registered_uri_set(req);
				}
			} else {
				log_file->write_header("::send_sip_tcp", LOG_NORMAL, LOG_WARNING);
				log_file->write_raw("Cannot find phone user for ");
				log_file->write_raw(req->hdr_to.uri.encode());
				log_file->write_endl();
				log_file->write_footer();
			}
		}
	}

	string m = sip_msg->encode();
	log_file->write_header("::send_sip_tcp", LOG_SIP);
	log_file->write_raw("Send to: tcp:");
	log_file->write_raw(h_ip2str(dst_addr));
	log_file->write_raw(":");
	log_file->write_raw(dst_port);
	log_file->write_endl();
	log_file->write_raw(m);
	log_file->write_endl();
	log_file->write_footer();
	
	conn->async_send(m.c_str(), m.size());
	
	if (new_connection) {
		connection_table->add_connection(conn);
	} else {
		connection_table->unlock();
	}
}