/** 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; }
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; }