/* $Id$ * * find & manage listen addresses * * Copyright (C) 2001-2003 FhG Fokus * * This file is part of ser, a free SIP server. * * ser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file contains code that initializes and handles ser listen addresses * lists (struct socket_info). It is used mainly on startup. * * History: * -------- * 2003-10-22 created by andrei * 2004-10-10 added grep_sock_info (andrei) * 2004-11-08 added find_si (andrei) * 2007-08-23 added detection for INADDR_ANY types of sockets (andrei) * 2008-08-08 sctp support (andrei) * 2008-08-15 support for handling sctp multihomed sockets (andrei) * 2008-10-15 fixed protocol list iteration when some protocols are * compile time disabled (andrei) */ /*! * \file * \brief SIP-router core :: * \ingroup core * Module: \ref core */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include "globals.h" #include "socket_info.h" #include "dprint.h" #include "mem/mem.h" #include "ut.h" #include "resolve.h" #include "name_alias.h" /* list manip. functions (internal use only) */ /* append */ #define sock_listadd(head, el) \ do{\ if (*(head)==0) *(head)=(el); \ else{ \ for((el)->next=*(head); (el)->next->next;\ (el)->next=(el)->next->next); \ (el)->next->next=(el); \ (el)->prev=(el)->next; \ (el)->next=0; \ }\ }while(0) /* insert after "after" */ #define sock_listins(el, after) \ do{ \ if ((after)){\ (el)->next=(after)->next; \ if ((after)->next) (after)->next->prev=(el); \ (after)->next=(el); \ (el)->prev=(after); \ }else{ /* after==0 = list head */ \ (after)=(el); \ (el)->next=(el)->prev=0; \ }\ }while(0) #define sock_listrm(head, el) \ do {\ if (*(head)==(el)) *(head)=(el)->next; \ if ((el)->next) (el)->next->prev=(el)->prev; \ if ((el)->prev) (el)->prev->next=(el)->next; \ }while(0) #define addr_info_listadd sock_listadd #define addr_info_listins sock_listins #define addr_info_listrm sock_listrm inline static void addr_info_list_ins_lst(struct addr_info* lst, struct addr_info* after) { struct addr_info* l; struct addr_info* n; if (lst){ n=after->next; after->next=lst; lst->prev=after; if (n){ for(l=lst; l->next; l=l->next); l->next=n; n->prev=l; } } } /* protocol order, filled by init_proto_order() */ enum sip_protos nxt_proto[PROTO_LAST+1]= { PROTO_UDP, PROTO_TCP, PROTO_TLS, PROTO_SCTP, 0 }; /* another helper function, it just fills a struct addr_info * returns: 0 on success, -1 on error*/ static int init_addr_info(struct addr_info* a, char* name, enum si_flags flags) { memset(a, 0, sizeof(*a)); a->name.len=strlen(name); a->name.s=pkg_malloc(a->name.len+1); /* include \0 */ if (a->name.s==0) goto error; memcpy(a->name.s, name, a->name.len+1); a->flags=flags; return 0; error: LOG(L_ERR, "ERROR: init_addr_info: memory allocation error\n"); return -1; } /* returns 0 on error, new addr_info_lst element on success */ static inline struct addr_info* new_addr_info(char* name, enum si_flags gf) { struct addr_info* al; al=pkg_malloc(sizeof(*al)); if (al==0) goto error; al->next=0; al->prev=0; if (init_addr_info(al, name, gf)!=0) goto error; return al; error: LOG(L_ERR, "ERROR: new_addr_info: memory allocation error\n"); if (al){ if (al->name.s) pkg_free(al->name.s); pkg_free(al); } return 0; } static inline void free_addr_info(struct addr_info* a) { if (a){ if (a->name.s){ pkg_free(a->name.s); a->name.s=0; } pkg_free(a); } } static inline void free_addr_info_lst(struct addr_info** lst) { struct addr_info* a; struct addr_info* tmp; a=*lst; while(a){ tmp=a; a=a->next; free_addr_info(tmp); } } /* adds a new add_info_lst element to the corresponding list * returns 0 on success, -1 on error */ static int new_addr_info2list(char* name, enum si_flags f, struct addr_info** l) { struct addr_info * al; al=new_addr_info(name, f); if (al==0) goto error; addr_info_listadd(l, al); return 0; error: return -1; } /* another helper function, it just creates a socket_info struct */ static inline struct socket_info* new_sock_info( char* name, struct name_lst* addr_l, unsigned short port, unsigned short proto, enum si_flags flags) { struct socket_info* si; struct name_lst* n; si=(struct socket_info*) pkg_malloc(sizeof(struct socket_info)); if (si==0) goto error; memset(si, 0, sizeof(struct socket_info)); si->socket=-1; si->name.len=strlen(name); si->name.s=(char*)pkg_malloc(si->name.len+1); /* include \0 */ if (si->name.s==0) goto error; memcpy(si->name.s, name, si->name.len+1); /* set port & proto */ si->port_no=port; si->proto=proto; si->flags=flags; si->addr_info_lst=0; for (n=addr_l; n; n=n->next){ if (new_addr_info2list(n->name, n->flags, &si->addr_info_lst)!=0){ LOG(L_ERR, "ERROR: new_sockk_info:new_addr_info2list failed\n"); goto error; } } return si; error: LOG(L_ERR, "ERROR: new_sock_info: memory allocation error\n"); if (si) pkg_free(si); return 0; } /* delete a socket_info struct */ static void free_sock_info(struct socket_info* si) { if(si){ if(si->name.s) pkg_free(si->name.s); if(si->address_str.s) pkg_free(si->address_str.s); if(si->port_no_str.s) pkg_free(si->port_no_str.s); if (si->addr_info_lst) free_addr_info_lst(&si->addr_info_lst); if(si->sock_str.s) pkg_free(si->sock_str.s); } } static char* get_proto_name(unsigned short proto) { switch(proto){ case PROTO_NONE: return "*"; case PROTO_UDP: return "udp"; #ifdef USE_TCP case PROTO_TCP: return "tcp"; #endif #ifdef USE_TLS case PROTO_TLS: return "tls"; #endif #ifdef USE_SCTP case PROTO_SCTP: return "sctp"; #endif default: return "unknown"; } } /** Convert socket to its textual representation. * * This function converts the transport protocol, the IP address and the port * number in a comma delimited string of form proto:ip:port. The resulting * string is NOT zero terminated * * @param s is a pointer to the destination memory buffer * @param len is a pointer to an integer variable. Initially the variable * should contain the size of the buffer in s. The value of the variable * will be changed to the length of the resulting string on success and * to the desired size of the destination buffer if it is too small * @param si is a pointer to the socket_info structure to be printed * @return -1 on error and 0 on success */ int socket2str(char* s, int* len, struct socket_info* si) { str proto; int l; proto.s = get_proto_name(si->proto); proto.len = strlen(proto.s); l = proto.len + si->address_str.len + si->port_no_str.len + 2; if(si->address.af==AF_INET6) l += 2; if (*len < l) { ERR("socket2str: Destionation buffer too short\n"); *len = l; return -1; } memcpy(s, proto.s, proto.len); s += proto.len; *s = ':'; s++; if(si->address.af==AF_INET6) { *s = '['; s++; } memcpy(s, si->address_str.s, si->address_str.len); s += si->address_str.len; if(si->address.af==AF_INET6) { *s = ']'; s++; } *s = ':'; s++; memcpy(s, si->port_no_str.s, si->port_no_str.len); s += si->port_no_str.len; *len = l; return 0; } /* Fill si->sock_str with string representing the socket_info structure, * format of the string is 'proto:address:port'. Returns 0 on success and * negative number on failure. */ static int fix_sock_str(struct socket_info* si) { int len = MAX_SOCKET_STR; if (si->sock_str.s) pkg_free(si->sock_str.s); si->sock_str.s = pkg_malloc(len + 1); if (si->sock_str.s == NULL) { ERR("fix_sock_str: No memory left\n"); return -1; } if (socket2str(si->sock_str.s, &len, si) < 0) { BUG("fix_sock_str: Error in socket2str\n"); return -1; } si->sock_str.s[len] = '\0'; si->sock_str.len = len; return 0; } /* returns 0 if support for the protocol is not compiled or if proto is invalid */ struct socket_info** get_sock_info_list(unsigned short proto) { switch(proto){ case PROTO_UDP: return &udp_listen; break; case PROTO_TCP: #ifdef USE_TCP return &tcp_listen; #endif break; case PROTO_TLS: #ifdef USE_TLS return &tls_listen; #endif break; case PROTO_SCTP: #ifdef USE_SCTP return &sctp_listen; #endif break; default: LOG(L_CRIT, "BUG: get_sock_info_list: invalid proto %d\n", proto); } return 0; } /* helper function for grep_sock_info * params: * host - hostname to compare with * name - official name * addr_str - name's resolved ip address converted to string * ip_addr - name's ip address * flags - set to SI_IS_IP if name contains an IP * * returns 0 if host matches, -1 if not */ inline static int si_hname_cmp(str* host, str* name, str* addr_str, struct ip_addr* ip_addr, int flags) { #ifdef USE_IPV6 struct ip_addr* ip6; #endif if ( (host->len==name->len) && (strncasecmp(host->s, name->s, name->len)==0) /*slower*/) /* comp. must be case insensitive, host names * can be written in mixed case, it will also match * ipv6 addresses if we are lucky*/ goto found; /* check if host == ip address */ #ifdef USE_IPV6 /* ipv6 case is uglier, host can be [3ffe::1] */ ip6=str2ip6(host); if (ip6){ if (ip_addr_cmp(ip6, ip_addr)) goto found; /* match */ else return -1; /* no match, but this is an ipv6 address so no point in trying ipv4 */ } #endif /* ipv4 */ if ( (!(flags&SI_IS_IP)) && (host->len==addr_str->len) && (memcmp(host->s, addr_str->s, addr_str->len)==0) ) goto found; return -1; found: return 0; } /* checks if the proto: host:port is one of the address we listen on * and returns the corresponding socket_info structure. * if port==0, the port number is ignored * if proto==0 (PROTO_NONE) the protocol is ignored * returns 0 if not found * WARNING: uses str2ip6 so it will overwrite any previous * unsaved result of this function (static buffer) */ struct socket_info* grep_sock_info(str* host, unsigned short port, unsigned short proto) { str hname; struct socket_info* si; struct socket_info** list; struct addr_info* ai; unsigned short c_proto; hname=*host; #ifdef USE_IPV6 if ((hname.len>2)&&((*hname.s)=='[')&&(hname.s[hname.len-1]==']')){ /* ipv6 reference, skip [] */ hname.s++; hname.len-=2; } #endif c_proto=(proto!=PROTO_NONE)?proto:PROTO_UDP; do{ /* get the proper sock_list */ list=get_sock_info_list(c_proto); if (list==0) /* disabled or unknown protocol */ continue; for (si=*list; si; si=si->next){ DBG("grep_sock_info - checking if host==us: %d==%d && " " [%.*s] == [%.*s]\n", hname.len, si->name.len, hname.len, hname.s, si->name.len, si->name.s ); if (port) { DBG("grep_sock_info - checking if port %d matches port %d\n", si->port_no, port); if (si->port_no!=port) { continue; } } if (si_hname_cmp(&hname, &si->name, &si->address_str, &si->address, si->flags)==0) goto found; /* try among the extra addresses */ for (ai=si->addr_info_lst; ai; ai=ai->next) if (si_hname_cmp(&hname, &ai->name, &ai->address_str, &ai->address, ai->flags)==0) goto found; } }while( (proto==0) && (c_proto=next_proto(c_proto)) ); /* not_found: */ return 0; found: return si; } /* checks if the proto:port is one of the ports we listen on * and returns the corresponding socket_info structure. * if proto==0 (PROTO_NONE) the protocol is ignored * returns 0 if not found */ struct socket_info* grep_sock_info_by_port(unsigned short port, unsigned short proto) { struct socket_info* si; struct socket_info** list; unsigned short c_proto; if (!port) { goto not_found; } c_proto=(proto!=PROTO_NONE)?proto:PROTO_UDP; do{ /* get the proper sock_list */ list=get_sock_info_list(c_proto); if (list==0) /* disabled or unknown protocol */ continue; for (si=*list; si; si=si->next){ DBG("grep_sock_info_by_port - checking if port %d matches" " port %d\n", si->port_no, port); if (si->port_no==port) { goto found; } } }while( (proto==0) && (c_proto=next_proto(c_proto)) ); not_found: return 0; found: return si; } /* checks if the proto: ip:port is one of the address we listen on * and returns the corresponding socket_info structure. * (same as grep_socket_info, but use ip addr instead) * if port==0, the port number is ignored * if proto==0 (PROTO_NONE) the protocol is ignored * returns 0 if not found * WARNING: uses str2ip6 so it will overwrite any previous * unsaved result of this function (static buffer) */ struct socket_info* find_si(struct ip_addr* ip, unsigned short port, unsigned short proto) { struct socket_info* si; struct socket_info** list; struct addr_info* ai; unsigned short c_proto; c_proto=(proto!=PROTO_NONE)?proto:PROTO_UDP; do{ /* get the proper sock_list */ list=get_sock_info_list(c_proto); if (list==0) /* disabled or unknown protocol */ continue; for (si=*list; si; si=si->next){ if (port) { if (si->port_no!=port) { continue; } } if (ip_addr_cmp(ip, &si->address)) goto found; for (ai=si->addr_info_lst; ai; ai=ai->next) if (ip_addr_cmp(ip, &ai->address)) goto found; } }while( (proto==0) && (c_proto=next_proto(c_proto)) ); /* not_found: */ return 0; found: return si; } /* append a new sock_info structure to the corresponding list * return new sock info on success, 0 on error */ static struct socket_info* new_sock2list(char* name, struct name_lst* addr_l, unsigned short port, unsigned short proto, enum si_flags flags, struct socket_info** list) { struct socket_info* si; si=new_sock_info(name, addr_l, port, proto, flags); if (si==0){ LOG(L_ERR, "ERROR: new_sock2list: new_sock_info failed\n"); goto error; } sock_listadd(list, si); return si; error: return 0; } /* adds a new sock_info structure immediately after "after" * return new sock info on success, 0 on error */ static struct socket_info* new_sock2list_after(char* name, struct name_lst* addr_l, unsigned short port, unsigned short proto, enum si_flags flags, struct socket_info* after) { struct socket_info* si; si=new_sock_info(name, addr_l, port, proto, flags); if (si==0){ LOG(L_ERR, "ERROR: new_sock2list_after: new_sock_info failed\n"); goto error; } sock_listins(si, after); return si; error: return 0; } /* adds a sock_info structure to the corresponding proto list * return 0 on success, -1 on error */ int add_listen_iface(char* name, struct name_lst* addr_l, unsigned short port, unsigned short proto, enum si_flags flags) { struct socket_info** list; unsigned short c_proto; struct name_lst* a_l; unsigned short c_port; c_proto=(proto!=PROTO_NONE)?proto:PROTO_UDP; do{ list=get_sock_info_list(c_proto); if (list==0) /* disabled or unknown protocol */ continue; if (port==0){ /* use default port */ c_port= #ifdef USE_TLS ((c_proto)==PROTO_TLS)?tls_port_no: #endif port_no; } #ifdef USE_TLS else if ((c_proto==PROTO_TLS) && (proto==0)){ /* -l ip:port => on udp:ip:port; tcp:ip:port and tls:ip:port+1?*/ c_port=port+1; } #endif else{ c_port=port; } if (c_proto!=PROTO_SCTP){ if (new_sock2list(name, 0, c_port, c_proto, flags & ~SI_IS_MHOMED, list)==0){ LOG(L_ERR, "ERROR: add_listen_iface: new_sock2list failed\n"); goto error; } /* add the other addresses in the list as separate sockets * since only SCTP can bind to multiple addresses */ for (a_l=addr_l; a_l; a_l=a_l->next){ if (new_sock2list(a_l->name, 0, c_port, c_proto, flags & ~SI_IS_MHOMED, list)==0){ LOG(L_ERR, "ERROR: add_listen_iface: new_sock2list" " failed\n"); goto error; } } }else{ if (new_sock2list(name, addr_l, c_port, c_proto, flags, list)==0){ LOG(L_ERR, "ERROR: add_listen_iface: new_sock2list failed\n"); goto error; } } }while( (proto==0) && (c_proto=next_proto(c_proto))); return 0; error: return -1; } /* add all family type addresses of interface if_name to the socket_info array * if if_name==0, adds all addresses on all interfaces * WARNING: it only works with ipv6 addresses on FreeBSD * return: -1 on error, 0 on success */ int add_interfaces(char* if_name, int family, unsigned short port, unsigned short proto, struct addr_info** ai_l) { struct ifconf ifc; struct ifreq ifr; struct ifreq ifrcopy; char* last; char* p; int size; int lastlen; int s; char* tmp; struct ip_addr addr; int ret; enum si_flags flags; #ifdef HAVE_SOCKADDR_SA_LEN #ifndef MAX #define MAX(a,b) ( ((a)>(b))?(a):(b)) #endif #endif /* ipv4 or ipv6 only*/ flags=SI_NONE; s=socket(family, SOCK_DGRAM, 0); ret=-1; lastlen=0; ifc.ifc_req=0; for (size=100; ; size*=2){ ifc.ifc_len=size*sizeof(struct ifreq); ifc.ifc_req=(struct ifreq*) pkg_malloc(size*sizeof(struct ifreq)); if (ifc.ifc_req==0){ LOG(L_ERR, "ERROR: add_interfaces: memory allocation failure\n"); goto error; } if (ioctl(s, SIOCGIFCONF, &ifc)==-1){ if(errno==EBADF) return 0; /* invalid descriptor => no such ifs*/ LOG(L_ERR, "ERROR: add_interfaces: ioctl failed: %s\n", strerror(errno)); goto error; } if ((lastlen) && (ifc.ifc_len==lastlen)) break; /*success, len not changed*/ lastlen=ifc.ifc_len; /* try a bigger array*/ pkg_free(ifc.ifc_req); } last=(char*)ifc.ifc_req+ifc.ifc_len; for(p=(char*)ifc.ifc_req; pifr_addr.sa_family);*/ continue; } /*get flags*/ ifrcopy=ifr; if (ioctl(s, SIOCGIFFLAGS, &ifrcopy)!=-1){ /* ignore errors */ /* ignore down ifs only if listening on all of them*/ if (if_name==0){ /* if if not up, skip it*/ if (!(ifrcopy.ifr_flags & IFF_UP)) continue; } } if ((if_name==0)|| (strncmp(if_name, ifr.ifr_name, sizeof(ifr.ifr_name))==0)){ /*add address*/ sockaddr2ip_addr(&addr, (struct sockaddr*)(p+(long)&((struct ifreq*)0)->ifr_addr)); if ((tmp=ip_addr2a(&addr))==0) goto error; /* check if loopback */ if (ifrcopy.ifr_flags & IFF_LOOPBACK) flags|=SI_IS_LO; /* save the info */ if (new_addr_info2list(tmp, flags, ai_l)!=0){ LOG(L_ERR, "ERROR: add_interfaces: " "new_addr_info2list failed\n"); goto error; } ret=0; } /* printf("%s:\n", ifr->ifr_name); printf(" "); print_sockaddr(&(ifr->ifr_addr)); printf(" "); ls_ifflags(ifr->ifr_name, family, options); printf("\n");*/ } pkg_free(ifc.ifc_req); /*clean up*/ close(s); return ret; error: if (ifc.ifc_req) pkg_free(ifc.ifc_req); close(s); return -1; } /* internal helper function: resolve host names and add aliases * name is a value result parameter: it should contain the hostname that * will be used to fill all the other members, including name itself * in some situation (name->s should be a 0 terminated pkg_malloc'ed string) * return 0 on success and -1 on error */ static int fix_hostname(str* name, struct ip_addr* address, str* address_str, enum si_flags* flags, int* type_flags, struct socket_info* s) { struct hostent* he; char* tmp; char** h; /* get "official hostnames", all the aliases etc. */ he=resolvehost(name->s); if (he==0){ LOG(L_ERR, "ERROR: fix_hostname: could not resolve %s\n", name->s); goto error; } /* check if we got the official name */ if (strcasecmp(he->h_name, name->s)!=0){ if (sr_auto_aliases && add_alias(name->s, name->len, s->port_no, s->proto)<0){ LOG(L_ERR, "ERROR: fix_hostname: add_alias failed\n"); } /* change the official name */ pkg_free(name->s); name->s=(char*)pkg_malloc(strlen(he->h_name)+1); if (name->s==0){ LOG(L_ERR, "ERROR: fix_hostname: out of memory.\n"); goto error; } name->len=strlen(he->h_name); strncpy(name->s, he->h_name, name->len+1); } /* add the aliases*/ for(h=he->h_aliases; sr_auto_aliases && h && *h; h++) if (add_alias(*h, strlen(*h), s->port_no, s->proto)<0){ LOG(L_ERR, "ERROR: fix_hostname: add_alias failed\n"); } hostent2ip_addr(address, he, 0); /*convert to ip_addr format*/ if (type_flags){ *type_flags|=(address->af==AF_INET)?SOCKET_T_IPV4:SOCKET_T_IPV6; } if ((tmp=ip_addr2a(address))==0) goto error; address_str->s=pkg_malloc(strlen(tmp)+1); if (address_str->s==0){ LOG(L_ERR, "ERROR: fix_hostname: out of memory.\n"); goto error; } strncpy(address_str->s, tmp, strlen(tmp)+1); /* set is_ip (1 if name is an ip address, 0 otherwise) */ address_str->len=strlen(tmp); if (sr_auto_aliases && (address_str->len==name->len) && (strncasecmp(address_str->s, name->s, address_str->len)==0)){ *flags|=SI_IS_IP; /* do rev. DNS on it (for aliases)*/ he=rev_resolvehost(address); if (he==0){ LOG(L_WARN, "WARNING: fix_hostname: could not rev. resolve %s\n", name->s); }else{ /* add the aliases*/ if (add_alias(he->h_name, strlen(he->h_name), s->port_no, s->proto)<0){ LOG(L_ERR, "ERROR: fix_hostname: add_alias failed\n"); } for(h=he->h_aliases; h && *h; h++) if (add_alias(*h, strlen(*h), s->port_no, s->proto) < 0){ LOG(L_ERR, "ERROR: fix_hostname: add_alias failed\n"); } } } #ifdef USE_MCAST /* Check if it is an multicast address and * set the flag if so */ if (is_mcast(address)){ *flags |= SI_IS_MCAST; } #endif /* USE_MCAST */ /* check if INADDR_ANY */ if (ip_addr_any(address)) *flags|=SI_IS_ANY; else if (ip_addr_loopback(address)) /* check for loopback */ *flags|=SI_IS_LO; return 0; error: return -1; } /* append new elements to a socket_info list after "list" * each element is created from addr_info_lst + port, protocol and flags * return 0 on succes, -1 on error */ static int addr_info_to_si_lst(struct addr_info* ai_lst, unsigned short port, char proto, enum si_flags flags, struct socket_info** list) { struct addr_info* ail; for (ail=ai_lst; ail; ail=ail->next){ if(new_sock2list(ail->name.s, 0, port, proto, ail->flags | flags, list)==0) return -1; } return 0; } /* insert new elements to a socket_info list after "el", * each element is created from addr_info_lst + port, * protocol and flags * return 0 on succes, -1 on error */ static int addr_info_to_si_lst_after(struct addr_info* ai_lst, unsigned short port, char proto, enum si_flags flags, struct socket_info* el) { struct addr_info* ail; struct socket_info* new_si; for (ail=ai_lst; ail; ail=ail->next){ if((new_si=new_sock2list_after(ail->name.s, 0, port, proto, ail->flags | flags, el))==0) return -1; el=new_si; } return 0; } /* fixes a socket list => resolve addresses, * interface names, fills missing members, remove duplicates * fills type_flags if not null with SOCKET_T_IPV4 and/or SOCKET_T_IPV6*/ static int fix_socket_list(struct socket_info **list, int* type_flags) { struct socket_info* si; struct socket_info* new_si; struct socket_info* l; struct socket_info* next; struct socket_info* next_si; struct socket_info* del_si; struct socket_info* keep_si; char* tmp; int len; struct addr_info* ai_lst; struct addr_info* ail; struct addr_info* tmp_ail; struct addr_info* tmp_ail_next; struct addr_info* ail_next; if (type_flags) *type_flags=0; /* try to change all the interface names into addresses * --ugly hack */ for (si=*list;si;){ next=si->next; ai_lst=0; if (add_interfaces(si->name.s, AF_INET, si->port_no, si->proto, &ai_lst)!=-1){ if (si->flags & SI_IS_MHOMED){ if((new_si=new_sock2list_after(ai_lst->name.s, 0, si->port_no, si->proto, ai_lst->flags|si->flags, si))==0) break; ail=ai_lst; ai_lst=ai_lst->next; free_addr_info(ail); /* free the first elem. */ if (ai_lst){ ai_lst->prev=0; /* find the end */ for (ail=ai_lst; ail->next; ail=ail->next); /* add the mh list after the last position in ai_lst */ addr_info_list_ins_lst(si->addr_info_lst, ail); new_si->addr_info_lst=ai_lst; si->addr_info_lst=0; /* detached and moved to new_si */ ail=ail->next; /* ail== old si->addr_info_lst */ }else{ ail=si->addr_info_lst; new_si->addr_info_lst=ail; si->addr_info_lst=0; /* detached and moved to new_si */ } }else{ /* add all addr. as separate interfaces */ if (addr_info_to_si_lst_after(ai_lst, si->port_no, si->proto, si->flags, si)!=0) goto error; /* ai_lst not needed anymore */ free_addr_info_lst(&ai_lst); ail=0; new_si=0; } /* success => remove current entry (shift the entire array)*/ sock_listrm(list, si); free_sock_info(si); }else{ new_si=si; ail=si->addr_info_lst; } if (ail){ if (new_si && (new_si->flags & SI_IS_MHOMED)){ ai_lst=0; for (; ail;){ ail_next=ail->next; if (add_interfaces(ail->name.s, AF_INET, new_si->port_no, new_si->proto, &ai_lst)!=-1){ /* add the resolved list after the current position */ addr_info_list_ins_lst(ai_lst, ail); /* success, remove the current entity */ addr_info_listrm(&new_si->addr_info_lst, ail); free_addr_info(ail); ai_lst=0; } ail=ail_next; } } } si=next; } /* get ips & fill the port numbers*/ #ifdef EXTRA_DEBUG DBG("Listening on \n"); #endif for (si=*list;si;si=si->next){ /* fix port number, port_no should be !=0 here */ if (si->port_no==0){ #ifdef USE_TLS si->port_no= (si->proto==PROTO_TLS)?tls_port_no:port_no; #else si->port_no= port_no; #endif } tmp=int2str(si->port_no, &len); if (len>=MAX_PORT_LEN){ LOG(L_ERR, "ERROR: fix_socket_list: bad port number: %d\n", si->port_no); goto error; } si->port_no_str.s=(char*)pkg_malloc(len+1); if (si->port_no_str.s==0){ LOG(L_ERR, "ERROR: fix_socket_list: out of memory.\n"); goto error; } strncpy(si->port_no_str.s, tmp, len+1); si->port_no_str.len=len; if (fix_hostname(&si->name, &si->address, &si->address_str, &si->flags, type_flags, si) !=0 ) goto error; /* fix hostnames in mh addresses */ for (ail=si->addr_info_lst; ail; ail=ail->next){ if (fix_hostname(&ail->name, &ail->address, &ail->address_str, &ail->flags, type_flags, si) !=0 ) goto error; } if (fix_sock_str(si) < 0) goto error; #ifdef EXTRA_DEBUG printf(" %.*s [%s]:%s%s\n", si->name.len, si->name.s, si->address_str.s, si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : ""); #endif } /* removing duplicate addresses*/ for (si=*list;si; ){ next_si=si->next; for (l=si->next;l;){ next=l->next; if ((si->port_no==l->port_no) && (si->address.af==l->address.af) && (memcmp(si->address.u.addr, l->address.u.addr, si->address.len) == 0) ){ /* remove the socket with no extra addresses., * if both of them have extra addresses, remove one of them * and merge the extra addresses into the other */ if (l->addr_info_lst==0){ del_si=l; keep_si=si; }else if (si->addr_info_lst==0){ del_si=si; keep_si=l; }else{ /* move l->addr_info_lst to si->addr_info_lst */ /* find last elem */ for (ail=si->addr_info_lst; ail->next; ail=ail->next); /* add the l list after the last position in si lst */ addr_info_list_ins_lst(l->addr_info_lst, ail); l->addr_info_lst=0; /* detached */ del_si=l; /* l will be removed */ keep_si=l; } #ifdef EXTRA_DEBUG printf("removing duplicate %s [%s] == %s [%s]\n", keep_si->name.s, keep_si->address_str.s, del_si->name.s, del_si->address_str.s); #endif /* add the name to the alias list*/ if ((!(del_si->flags& SI_IS_IP)) && ( (del_si->name.len!=keep_si->name.len)|| (strncmp(del_si->name.s, keep_si->name.s, del_si->name.len)!=0)) ) add_alias(del_si->name.s, del_si->name.len, l->port_no, l->proto); /* make sure next_si doesn't point to del_si */ if (del_si==next_si) next_si=next_si->next; /* remove del_si*/ sock_listrm(list, del_si); free_sock_info(del_si); } l=next; } si=next_si; } /* check for duplicates in extra_addresses */ for (si=*list;si; si=si->next){ /* check for & remove internal duplicates: */ for (ail=si->addr_info_lst; ail;){ ail_next=ail->next; /* 1. check if the extra addresses contain a duplicate for the * main one */ if ((ail->address.af==si->address.af) && (memcmp(ail->address.u.addr, si->address.u.addr, ail->address.len) == 0)){ /* add the name to the alias list*/ if ((!(ail->flags& SI_IS_IP)) && ( (ail->name.len!=si->name.len)|| (strncmp(ail->name.s, si->name.s, ail->name.len)!=0))) add_alias(ail->name.s, ail->name.len, si->port_no, si->proto); /* remove ail*/ addr_info_listrm(&si->addr_info_lst, ail); free_addr_info(ail); ail=ail_next; continue; } /* 2. check if the extra addresses contain a duplicates for * other addresses in the same list */ for (tmp_ail=ail->next; tmp_ail;){ tmp_ail_next=tmp_ail->next; if ((ail->address.af==tmp_ail->address.af) && (memcmp(ail->address.u.addr, tmp_ail->address.u.addr, ail->address.len) == 0)){ /* add the name to the alias list*/ if ((!(tmp_ail->flags& SI_IS_IP)) && ( (ail->name.len!=tmp_ail->name.len)|| (strncmp(ail->name.s, tmp_ail->name.s, tmp_ail->name.len)!=0)) ) add_alias(tmp_ail->name.s, tmp_ail->name.len, si->port_no, si->proto); /* remove tmp_ail*/ addr_info_listrm(&si->addr_info_lst, tmp_ail); free_addr_info(tmp_ail); } tmp_ail=tmp_ail_next; } ail=ail_next; } /* check for duplicates between extra addresses (e.g. sctp MH) * and other main addresses, on conflict remove the corresponding * extra addresses (another possible solution would be to join * the 2 si entries into one). */ for (ail=si->addr_info_lst; ail;){ ail_next=ail->next; for (l=*list;l; l=l->next){ if (l==si) continue; if (si->port_no==l->port_no){ if ((ail->address.af==l->address.af) && (memcmp(ail->address.u.addr, l->address.u.addr, ail->address.len) == 0)){ /* add the name to the alias list*/ if ((!(ail->flags& SI_IS_IP)) && ( (ail->name.len!=l->name.len)|| (strncmp(ail->name.s, l->name.s, l->name.len)!=0)) ) add_alias(ail->name.s, ail->name.len, l->port_no, l->proto); /* remove ail*/ addr_info_listrm(&si->addr_info_lst, ail); free_addr_info(ail); break; } /* check for duplicates with other extra addresses * lists */ for (tmp_ail=l->addr_info_lst; tmp_ail; ){ tmp_ail_next=tmp_ail->next; if ((ail->address.af==tmp_ail->address.af) && (memcmp(ail->address.u.addr, tmp_ail->address.u.addr, ail->address.len) == 0)){ /* add the name to the alias list*/ if ((!(tmp_ail->flags& SI_IS_IP)) && ( (ail->name.len!=tmp_ail->name.len)|| (strncmp(ail->name.s, tmp_ail->name.s, tmp_ail->name.len)!=0)) ) add_alias(tmp_ail->name.s, tmp_ail->name.len, l->port_no, l->proto); /* remove tmp_ail*/ addr_info_listrm(&l->addr_info_lst, tmp_ail); free_addr_info(tmp_ail); } tmp_ail=tmp_ail_next; } } } ail=ail_next; } } #ifdef USE_MCAST /* Remove invalid multicast entries */ si=*list; while(si){ if ((si->proto == PROTO_TCP) #ifdef USE_TLS || (si->proto == PROTO_TLS) #endif /* USE_TLS */ #ifdef USE_SCTP || (si->proto == PROTO_SCTP) #endif ){ if (si->flags & SI_IS_MCAST){ LOG(L_WARN, "WARNING: removing entry %s:%s [%s]:%s\n", get_proto_name(si->proto), si->name.s, si->address_str.s, si->port_no_str.s); l = si; si=si->next; sock_listrm(list, l); free_sock_info(l); }else{ ail=si->addr_info_lst; while(ail){ if (ail->flags & SI_IS_MCAST){ LOG(L_WARN, "WARNING: removing mh entry %s:%s" " [%s]:%s\n", get_proto_name(si->proto), ail->name.s, ail->address_str.s, si->port_no_str.s); tmp_ail=ail; ail=ail->next; addr_info_listrm(&si->addr_info_lst, tmp_ail); free_addr_info(tmp_ail); }else{ ail=ail->next; } } si=si->next; } } else { si=si->next; } } #endif /* USE_MCAST */ return 0; error: return -1; } int socket_types = 0; /* fix all 3 socket lists, fills socket_types if non-null * return 0 on success, -1 on error */ int fix_all_socket_lists() { struct utsname myname; int flags; struct addr_info* ai_lst; ai_lst=0; if ((udp_listen==0) #ifdef USE_TCP && (tcp_listen==0) #ifdef USE_TLS && (tls_listen==0) #endif #endif #ifdef USE_SCTP && (sctp_listen==0) #endif ){ /* get all listening ipv4 interfaces */ if ((add_interfaces(0, AF_INET, 0, PROTO_UDP, &ai_lst)==0) && (addr_info_to_si_lst(ai_lst, 0, PROTO_UDP, 0, &udp_listen)==0)){ free_addr_info_lst(&ai_lst); ai_lst=0; /* if ok, try to add the others too */ #ifdef USE_TCP if (!tcp_disable){ if ((add_interfaces(0, AF_INET, 0, PROTO_TCP, &ai_lst)!=0) || (addr_info_to_si_lst(ai_lst, 0, PROTO_TCP, 0, &tcp_listen)!=0)) goto error; free_addr_info_lst(&ai_lst); ai_lst=0; #ifdef USE_TLS if (!tls_disable){ if ((add_interfaces(0, AF_INET, 0, PROTO_TLS, &ai_lst)!=0) || (addr_info_to_si_lst(ai_lst, 0, PROTO_TLS, 0, &tls_listen)!=0)) goto error; } free_addr_info_lst(&ai_lst); ai_lst=0; #endif } #endif #ifdef USE_SCTP if (!sctp_disable){ if ((add_interfaces(0, AF_INET, 0, PROTO_SCTP, &ai_lst)!=0)|| (addr_info_to_si_lst(ai_lst, 0, PROTO_SCTP, 0, &sctp_listen)!=0)) goto error; free_addr_info_lst(&ai_lst); ai_lst=0; } #endif /* USE_SCTP */ }else{ /* if error fall back to get hostname */ /* get our address, only the first one */ if (uname (&myname) <0){ LOG(L_ERR, "ERROR: fix_all_socket_lists: cannot determine" " hostname, try -l address\n"); goto error; } if (add_listen_iface(myname.nodename, 0, 0, 0, 0)!=0){ LOG(L_ERR, "ERROR: fix_all_socket_lists: add_listen_iface " "failed \n"); goto error; } } } flags=0; if (fix_socket_list(&udp_listen, &flags)!=0){ LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list" " udp failed\n"); goto error; } if (flags){ socket_types|=flags|SOCKET_T_UDP; } #ifdef USE_TCP flags=0; if (!tcp_disable && (fix_socket_list(&tcp_listen, &flags)!=0)){ LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list" " tcp failed\n"); goto error; } if (flags){ socket_types|=flags|SOCKET_T_TCP; } #ifdef USE_TLS flags=0; if (!tls_disable && (fix_socket_list(&tls_listen, &flags)!=0)){ LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list" " tls failed\n"); goto error; } if (flags){ socket_types|=flags|SOCKET_T_TLS; } #endif #endif #ifdef USE_SCTP flags=0; if (!sctp_disable && (fix_socket_list(&sctp_listen, &flags)!=0)){ LOG(L_ERR, "ERROR: fix_all_socket_lists: fix_socket_list" " sctp failed\n"); goto error; } if (flags){ socket_types|=flags|SOCKET_T_SCTP; } #endif /* USE_SCTP */ if ((udp_listen==0) #ifdef USE_TCP && (tcp_listen==0) #ifdef USE_TLS && (tls_listen==0) #endif #endif #ifdef USE_SCTP && (sctp_listen==0) #endif ){ LOG(L_ERR, "ERROR: fix_all_socket_lists: no listening sockets\n"); goto error; } return 0; error: if (ai_lst) free_addr_info_lst(&ai_lst); return -1; } void print_all_socket_lists() { struct socket_info *si; struct socket_info** list; struct addr_info* ai; unsigned short proto; proto=PROTO_UDP; do{ list=get_sock_info_list(proto); for(si=list?*list:0; si; si=si->next){ if (si->addr_info_lst){ printf(" %s: (%s", get_proto_name(proto), si->address_str.s); for (ai=si->addr_info_lst; ai; ai=ai->next) printf(", %s", ai->address_str.s); printf("):%s%s%s\n", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); }else{ printf(" %s: %s", get_proto_name(proto), si->name.s); if (!si->flags & SI_IS_IP) printf(" [%s]", si->address_str.s); printf( ":%s%s%s\n", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); } } }while((proto=next_proto(proto))); } void print_aliases() { struct host_alias* a; for(a=aliases; a; a=a->next) if (a->port) printf(" %s: %.*s:%d\n", get_proto_name(a->proto), a->alias.len, a->alias.s, a->port); else printf(" %s: %.*s:*\n", get_proto_name(a->proto), a->alias.len, a->alias.s); } void init_proto_order() { int r; /* fix proto list (remove disabled protocols)*/ #ifdef USE_TCP if (tcp_disable) #endif for(r=PROTO_NONE; r<=PROTO_LAST; r++){ if (nxt_proto[r]==PROTO_TCP) nxt_proto[r]=nxt_proto[PROTO_TCP]; } #ifdef USE_TCP #ifdef USE_TLS if (tls_disable || tcp_disable) #endif #endif for(r=PROTO_NONE; r<=PROTO_LAST; r++){ if (nxt_proto[r]==PROTO_TLS) nxt_proto[r]=nxt_proto[PROTO_TLS]; } #ifdef USE_SCTP if (sctp_disable) #endif for(r=PROTO_NONE; r<=PROTO_LAST; r++){ if (nxt_proto[r]==PROTO_SCTP) nxt_proto[r]=nxt_proto[PROTO_SCTP]; } }