Exemplo n.º 1
0
/** get the sending socket for a corresponding destination.
 * @param force_send_socket - if !=0 and the protocol and af correspond
 *                            with the destination, it will be returned.
 *                            If the protocol or af check fail, a look-alike
 *                            socket will be searched for and mismatch will be
 *                            set. If no look-alike socket is found it will
 *                            fallback to normal resolution.
 * @param to - destination
 * @param proto - protocol
 * @param mismatch - result parameter, set if a force_send_socket was used, but
 *                   there was an error matching it exactly to the destination.
 *                   Possible values: 0 ok, SS_MISMATCH_PROTO,
 *                   SS_MISMATCH_ADDR, SS_MISMATCH_AF, SS_MISMATCH_MCAST.
 * @return a socket_info pointer to the sending socket on success (and possibly
 *         sets mismatch) or 0 on error.
 */
struct socket_info* get_send_socket2(struct socket_info* force_send_socket,
										union sockaddr_union* to, int proto,
										enum ss_mismatch* mismatch)
{
	struct socket_info* send_sock;
	struct socket_info* orig;
	
	if (likely(mismatch)) *mismatch=0;
	/* check if send interface is not forced */
	if (unlikely(force_send_socket)){
		orig=force_send_socket;
		/* Special case here as there is no ;transport=wss - so wss connections will
		   appear as ws ones and be sorted out in the WebSocket module */
		if (unlikely(orig->proto!=proto && !(orig->proto==PROTO_TLS && proto==PROTO_WS))){
			force_send_socket=find_si(&(force_send_socket->address),
											force_send_socket->port_no,
											proto);
			if (unlikely(force_send_socket == 0)){
				if (likely(mismatch)) *mismatch=SS_MISMATCH_ADDR;
				LOG(L_WARN, "WARNING: get_send_socket: "
						"protocol/port mismatch (forced %s:%s:%d,"
						" to %s:%s)\n",
						proto2a(orig->proto), ip_addr2a(&orig->address),
						orig->port_no,
						proto2a(proto), su2a(to, sizeof(*to)));
				goto not_forced;
			}
			if (likely(mismatch)) *mismatch=SS_MISMATCH_PROTO;
		}
		if (unlikely(force_send_socket->address.af!=to->s.sa_family)){
			DBG("get_send_socket: force_send_socket of different af"
					" (dst %d - %s:%s forced %d -%s:%s:%d)\n",
					to->s.sa_family, proto2a(proto), su2a(to, sizeof(*to)),
					force_send_socket->address.af,
					proto2a(force_send_socket->proto),
					ip_addr2a(&force_send_socket->address),
					force_send_socket->port_no);
			if (likely(mismatch)) *mismatch=SS_MISMATCH_AF;
			goto not_forced;
		}
		/* check if listening on the socket (the check does not work
		   for TCP and TLS, for them socket==-1 on all the processes
		   except tcp_main(), see close_extra_socks() */
		if (likely((force_send_socket->socket!=-1 ||
						force_send_socket->proto==PROTO_TCP ||
						force_send_socket->proto==PROTO_TLS ||
						force_send_socket->proto==PROTO_WS  ||
						force_send_socket->proto==PROTO_WSS) &&
					!(force_send_socket->flags & SI_IS_MCAST)))
				return force_send_socket;
		else{
			if (!(force_send_socket->flags & SI_IS_MCAST))
				LOG(L_WARN, "WARNING: get_send_socket: not listening"
							 " on the requested socket (%s:%s:%d),"
							 " no fork mode?\n",
							proto2a(force_send_socket->proto),
							ip_addr2a(&force_send_socket->address),
							force_send_socket->port_no);
			else if (likely(mismatch)) *mismatch=SS_MISMATCH_MCAST;
		}
	};
not_forced:
	if (mhomed && proto==PROTO_UDP){
		send_sock=get_out_socket(to, proto);
		if ((send_sock==0) || (send_sock->socket!=-1))
			return send_sock; /* found or error*/
		else if (send_sock->socket==-1){
			LOG(L_WARN, "WARNING: get_send_socket: not listening on the"
					" requested socket (%s:%s:%d), no fork mode?\n",
					proto2a(send_sock->proto), ip_addr2a(&send_sock->address),
					send_sock->port_no);
			/* continue: try to use some socket */
		}
	}

	send_sock=0;
	/* check if we need to change the socket (different address families -
	 * eg: ipv4 -> ipv6 or ipv6 -> ipv4) */
	switch(proto){
#ifdef USE_TCP
		case PROTO_WS:
		case PROTO_TCP:
		/* on tcp just use the "main address", we don't really now the
		 * sending address (we can find it out, but we'll need also to see
		 * if we listen on it, and if yes on which port -> too complicated*/
			switch(to->s.sa_family){
				/* FIXME */
				case AF_INET:	send_sock=sendipv4_tcp;
								break;
#ifdef USE_IPV6
				case AF_INET6:	send_sock=sendipv6_tcp;
								break;
#endif
				default:	LOG(L_ERR, "get_send_socket: BUG: don't know how"
									" to forward to af %d\n", to->s.sa_family);
			}
			break;
#endif
#ifdef USE_TLS
		case PROTO_WSS:
		case PROTO_TLS:
			switch(to->s.sa_family){
				/* FIXME */
				case AF_INET:	send_sock=sendipv4_tls;
								break;
#ifdef USE_IPV6
				case AF_INET6:	send_sock=sendipv6_tls;
								break;
#endif
				default:	LOG(L_ERR, "get_send_socket: BUG: don't know how"
									" to forward to af %d\n", to->s.sa_family);
			}
			break;
#endif /* USE_TLS */
#ifdef USE_SCTP
		case PROTO_SCTP:
			if ((bind_address==0) ||
					(to->s.sa_family!=bind_address->address.af) ||
					(bind_address->proto!=PROTO_SCTP)){
				switch(to->s.sa_family){
					case AF_INET:	send_sock=sendipv4_sctp;
									break;
#ifdef USE_IPV6
					case AF_INET6:	send_sock=sendipv6_sctp;
									break;
#endif
					default:	LOG(L_ERR, "get_send_socket: BUG: don't know"
										" how to forward to af %d\n",
										to->s.sa_family);
				}
			}else send_sock=bind_address;
			break;
#endif /* USE_SCTP */
		case PROTO_UDP:
			if ((bind_address==0) ||
					(to->s.sa_family!=bind_address->address.af) ||
					(bind_address->proto!=PROTO_UDP)){
				switch(to->s.sa_family){
					case AF_INET:	send_sock=sendipv4;
									break;
#ifdef USE_IPV6
					case AF_INET6:	send_sock=sendipv6;
									break;
#endif
					default:	LOG(L_ERR, "get_send_socket: BUG: don't know"
										" how to forward to af %d\n",
										to->s.sa_family);
				}
			}else send_sock=bind_address;
			break;
		default:
			LOG(L_CRIT, "BUG: get_send_socket: unsupported proto %d (%s)\n",
					proto, proto2a(proto));
	}
	return send_sock;
}
Exemplo n.º 2
0
struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
{
	int* temp_sock;
	socklen_t len;
	union sockaddr_union from;
	struct socket_info* si;
	struct ip_addr ip;
	union sockaddr_union uncon;

	memset(&uncon, 0, sizeof(union sockaddr_union));
	uncon.sin.sin_family = AF_UNSPEC;

	if (unlikely(proto!=PROTO_UDP)) {
		LM_CRIT("can only be called for UDP\n");
		return 0;
	}
retry:
	switch(to->s.sa_family){
	case AF_INET : {
		if(unlikely(sock_inet < 0)){
			sock_inet = socket(AF_INET, SOCK_DGRAM, 0);
			if (sock_inet==-1) {
				LM_ERR("socket() failed: %s\n", strerror(errno));
				return 0;
			}
		}
		temp_sock = &sock_inet;
		break;
	}
	case AF_INET6 : {
		if(unlikely(sock_inet6 < 0)){
			sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
			if (sock_inet6==-1) {
				LM_ERR("socket() failed: %s\n", strerror(errno));
				return 0;
			}
		}
		temp_sock = &sock_inet6;
		break;
	}
	default: {
		LM_ERR("Unknown protocol family \n");
		return 0;
	}
	}

	if( !mhomed_sock_cache_disabled ){
		/* some Linux kernel versions (all?) along with other UNIXes don't re-bound the sock if already bound */
		/* to un-bound a socket set sin_family to AF_UNSPEC and zero out the rest*/
		if (unlikely(connect(*temp_sock, &uncon.s, sockaddru_len(uncon)) < 0))
				mhomed_sock_cache_disabled = 1;
	}

	if (unlikely(connect(*temp_sock, &to->s, sockaddru_len(*to))==-1)) {
		if (unlikely(errno==EISCONN && !mhomed_sock_cache_disabled)){
			/*  no multiple connects support on the same socket */
			mhomed_sock_cache_disabled=1;
			if (sock_inet>=0){
				close(sock_inet);
				sock_inet=-1;
			}
			if (sock_inet6>=0){
				close(sock_inet6);
				sock_inet6=-1;
			}
			goto retry;
		}
		LM_ERR("connect failed: %s\n", strerror(errno));
		goto error;
	}
	len=sizeof(from);
	if (unlikely(getsockname(*temp_sock, &from.s, &len)==-1)) {
		LM_ERR("getsockname failed: %s\n", strerror(errno));
		goto error;
	}
	su2ip_addr(&ip, &from);
	si=find_si(&ip, 0, proto);
	if (si==0) goto error;
	LM_DBG("socket determined: %p\n", si );
	if (unlikely(mhomed_sock_cache_disabled)){
		close(*temp_sock);
		*temp_sock=-1;
	}
	return si;
error:
	LM_ERR("no socket found\n");
	ERR("no corresponding socket found for(%s:%s)\n",
			proto2a(proto), su2a(to, sizeof(*to)));
	if (unlikely(mhomed_sock_cache_disabled && *temp_sock >=0)){
		close(*temp_sock);
		*temp_sock=-1;
	}
	return 0;
}