/* $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, char *usename, unsigned short useport, enum si_flags flags) { struct socket_info* si; struct name_lst* n; struct hostent* he; char *p; 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; } } if(usename!=NULL) { si->useinfo.name.len=strlen(usename); si->useinfo.name.s=(char*)pkg_malloc(si->useinfo.name.len+1); if (si->useinfo.name.s==0) goto error; strcpy(si->useinfo.name.s, usename); if(usename[0]=='[' && usename[si->useinfo.name.len-1]==']') { si->useinfo.address_str.len = si->useinfo.name.len - 2; p = si->useinfo.name.s + 1; } else { si->useinfo.address_str.len = si->useinfo.name.len; p = si->useinfo.name.s; } si->useinfo.address_str.s=(char*)pkg_malloc(si->useinfo.address_str.len+1); if(si->useinfo.address_str.s==NULL) goto error; strncpy(si->useinfo.address_str.s, p, si->useinfo.address_str.len); si->useinfo.address_str.s[si->useinfo.address_str.len] = '\0'; p = int2str(useport, &si->useinfo.port_no_str.len); if(p==NULL) goto error; si->useinfo.port_no_str.s=(char*)pkg_malloc(si->useinfo.port_no_str.len+1); if(si->useinfo.port_no_str.s==NULL) goto error; strcpy(si->useinfo.port_no_str.s, p); si->useinfo.port_no = useport; he=resolvehost(si->useinfo.name.s); if (he==0){ LM_ERR(" unable to resolve advertised name %s\n", si->useinfo.name.s); goto error; } hostent2ip_addr(&si->useinfo.address, he, 0); } return si; error: LOG(L_ERR, "ERROR: new_sock_info: memory allocation error\n"); if (si) { if(si->name.s) pkg_free(si->name.s); 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); if(si->useinfo.name.s) pkg_free(si->useinfo.name.s); if(si->useinfo.port_no_str.s) pkg_free(si->useinfo.port_no_str.s); if(si->useinfo.sock_str.s) pkg_free(si->useinfo.sock_str.s); } } static char* get_valid_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) { return socketinfo2str(s, len, si, 0); } int socketinfo2str(char* s, int* len, struct socket_info* si, int mode) { str proto; int l; proto.s = get_valid_proto_name(si->proto); proto.len = strlen(proto.s); if(mode==1) l = proto.len + si->useinfo.name.len + si->useinfo.port_no_str.len + 2; else l = proto.len + si->address_str.len + si->port_no_str.len + 2; if(si->address.af==AF_INET6) l += 2; if (*len < l) { LM_ERR("Destionation buffer too short\n"); *len = l; return -1; } memcpy(s, proto.s, proto.len); s += proto.len; *s = ':'; s++; if(mode==1){ memcpy(s, si->useinfo.name.s, si->useinfo.name.len); s += si->useinfo.name.len; *s = ':'; s++; memcpy(s, si->useinfo.port_no_str.s, si->useinfo.port_no_str.len); s += si->useinfo.port_no_str.len; } else { 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 (socketinfo2str(si->sock_str.s, &len, si, 0) < 0) { BUG("fix_sock_str: Error in socket to str\n"); return -1; } si->sock_str.s[len] = '\0'; si->sock_str.len = len; if(si->useinfo.name.s!=NULL) { len = MAX_SOCKET_STR; if (si->useinfo.sock_str.s) pkg_free(si->useinfo.sock_str.s); si->useinfo.sock_str.s = pkg_malloc(len + 1); if (si->useinfo.sock_str.s == NULL) { ERR("fix_sock_str: No memory left\n"); return -1; } if (socketinfo2str(si->useinfo.sock_str.s, &len, si, 1) < 0) { BUG("fix_sock_str: Error in socket to str\n"); return -1; } si->useinfo.sock_str.s[len] = '\0'; si->useinfo.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 (advertise %d)" " matches port %d\n", si->port_no, si->useinfo.port_no, port); if (si->port_no!=port && si->useinfo.port_no!=port) { continue; } } if (si_hname_cmp(&hname, &si->name, &si->address_str, &si->address, si->flags)==0) goto found; if(si->useinfo.name.s!=NULL) { DBG("grep_sock_info - checking advertise if host==us:" " %d==%d && [%.*s] == [%.*s]\n", hname.len, si->useinfo.name.len, hname.len, hname.s, si->useinfo.name.len, si->useinfo.name.s ); if (si_hname_cmp(&hname, &si->useinfo.name, &si->useinfo.address_str, &si->useinfo.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) || ip_addr_cmp(ip, &si->useinfo.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, char *usename, unsigned short useport, enum si_flags flags, struct socket_info** list) { struct socket_info* si; si=new_sock_info(name, addr_l, port, proto, usename, useport, flags); if (si==0){ LOG(L_ERR, "ERROR: new_sock2list: new_sock_info failed\n"); goto error; } if(socket_workers>0) { si->workers = socket_workers; socket_workers = 0; } 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, char *usename, unsigned short useport, enum si_flags flags, struct socket_info* after) { struct socket_info* si; si=new_sock_info(name, addr_l, port, proto, usename, useport, 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_advertise_iface(char* name, struct name_lst* addr_l, unsigned short port, unsigned short proto, char *usename, unsigned short useport, 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, usename, useport, 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, usename, useport, 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, usename, useport, 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; } /* 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) { return add_listen_advertise_iface(name, addr_l, port, proto, 0, 0, flags); } #ifdef __OS_linux #include "linux/netlink.h" #include "linux/rtnetlink.h" #include "arpa/inet.h" #define MAX_IF_LEN 64 struct idx { struct idx * next; int family; unsigned ifa_flags; char addr[MAX_IF_LEN]; }; struct idxlist{ struct idx* addresses; int index; char name[MAX_IF_LEN]; unsigned flags; }; #define MAX_IFACE_NO 32 static struct idxlist *ifaces = NULL; static int seq = 0; #define SADDR(s) ((struct sockaddr_in*)s)->sin_addr.s_addr #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); return -1; } rta = NLMSG_TAIL(n); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return 0; } static int nl_bound_sock(void) { int sock; struct sockaddr_nl la; sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if(sock <= 0){ LM_ERR("could not create NETLINK sock to get interface list"); goto error; } /* bind NETLINK socket to pid */ bzero(&la, sizeof(la)); la.nl_family = AF_NETLINK; la.nl_pad = 0; la.nl_pid = getpid(); la.nl_groups = 0; if ( bind(sock, (struct sockaddr*) &la, sizeof(la)) < 0){ LM_ERR("could not bind NETLINK sock to sockaddr_nl\n"); goto error; } return sock; error: if(sock > 0) close(sock); return -1; } #define fill_nl_req(req, type, family) do {\ memset(&req, 0, sizeof(req));\ req.nlh.nlmsg_len = sizeof(req);\ req.nlh.nlmsg_type = type;\ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST|NLM_F_DUMP;\ req.nlh.nlmsg_pid = getpid();\ req.nlh.nlmsg_seq = seq++;\ req.g.rtgen_family = family;\ } while(0); static int get_flags(int family){ struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; int rtn = 0; struct nlmsghdr* nlp; struct ifinfomsg *ifi; char buf[8192]; char *p = buf; int nll = 0; int nl_sock = 0; fill_nl_req(req, RTM_GETLINK, AF_INET); if((nl_sock = nl_bound_sock()) < 0) return -1; if(send(nl_sock, (void*)&req, sizeof(req), 0) < 0) { LM_ERR("error sending NETLINK request\n"); goto error; } while(1) { rtn = recv(nl_sock, p, sizeof(buf) - nll, 0); nlp = (struct nlmsghdr *) p; if(nlp->nlmsg_type == NLMSG_DONE){ LM_DBG("done\n"); break; } if(nlp->nlmsg_type == NLMSG_ERROR){ LM_DBG("Error on message to netlink"); break; } p += rtn; nll += rtn; } nlp = (struct nlmsghdr *) buf; for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll)){ ifi = NLMSG_DATA(nlp); if (nlp->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) goto error; LM_ERR("Interface with index %d has flags %d\n", ifi->ifi_index, ifi->ifi_flags); if(ifaces == NULL){ LM_ERR("get_flags must not be called on empty interface list"); goto error; } if(ifi->ifi_index >= MAX_IFACE_NO){ LM_ERR("invalid network interface index returned %d", ifi->ifi_index); goto error; } ifaces[ifi->ifi_index].flags = ifi->ifi_flags; } if(nl_sock>0) close(nl_sock); return 0; error: if(nl_sock>0) close(nl_sock); return -1; } static int build_iface_list(void) { struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; int seq = 0; int rtn = 0; struct nlmsghdr* nlp; struct ifaddrmsg *ifi; int rtl; char buf[8192]; char *p = buf; int nll = 0; struct rtattr * rtap; int index, i; struct idx* entry; struct idx* tmp; int nl_sock = 0; int families[] = {AF_INET, AF_INET6}; char name[MAX_IF_LEN]; int is_link_local = 0; if(ifaces == NULL){ if((ifaces = (struct idxlist*)pkg_malloc(MAX_IFACE_NO*sizeof(struct idxlist))) == NULL){ LM_ERR("No more pkg memory\n"); return -1; } memset(ifaces, 0, sizeof(struct idxlist)*MAX_IFACE_NO); } /* bind netlink socket */ if((nl_sock = nl_bound_sock()) < 0) return -1; for (i = 0 ; i < sizeof(families)/sizeof(int); i++) { fill_nl_req(req, RTM_GETADDR, families[i]); if(send(nl_sock, (void*)&req, sizeof(req), 0) < 0){ LM_ERR("error sending NETLINK request\n"); goto error; }; memset(buf, 0, sizeof(buf)); nll = 0; p = buf; while(1) { rtn = recv(nl_sock, p, sizeof(buf) - nll, 0); LM_DBG("received %d byles \n", rtn); nlp = (struct nlmsghdr *) p; if(nlp->nlmsg_type == NLMSG_DONE){ LM_DBG("done receiving netlink info \n"); break; } if(nlp->nlmsg_type == NLMSG_ERROR){ LM_ERR("Error on message to netlink"); break; } p += rtn; nll += rtn; } nlp = (struct nlmsghdr *) buf; for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll)){ ifi = NLMSG_DATA(nlp); if (nlp->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) continue; // init all the strings // inner loop: loop thru all the attributes of // one route entry rtap = (struct rtattr *) IFA_RTA(ifi); rtl = IFA_PAYLOAD(nlp); index = ifi->ifa_index; if(index >= MAX_IFACE_NO){ LM_ERR("Invalid interface index returned: %d\n", index); goto error; } entry = (struct idx*)pkg_malloc(sizeof(struct idx)); if(entry == 0) { LM_ERR("could not allocate memory\n"); goto error; } entry->next = 0; entry->family = families[i]; entry->ifa_flags = ifi->ifa_flags; is_link_local = 0; for(;RTA_OK(rtap, rtl);rtap=RTA_NEXT(rtap,rtl)){ switch(rtap->rta_type){ case IFA_ADDRESS: if((*(int*)RTA_DATA(rtap))== htons(0xfe80)){ LM_DBG("Link Local Address, ignoring ...\n"); is_link_local = 1; break; } inet_ntop(families[i], RTA_DATA(rtap), entry->addr, MAX_IF_LEN); LM_DBG("iface addr is %s\n", entry->addr); break; case IFA_LOCAL: if((*(int*)RTA_DATA(rtap))== htons(0xfe80)){ LM_DBG("Link Local Address, ignoring ...\n"); is_link_local = 1; } inet_ntop(families[i], RTA_DATA(rtap), entry->addr, MAX_IF_LEN); LM_DBG("iface addr is %s\n", entry->addr); break; case IFA_LABEL: LM_DBG("iface name is %s\n", (char*)RTA_DATA(rtap)); strncpy(name, (char*)RTA_DATA(rtap), MAX_IF_LEN); break; case IFA_BROADCAST: case IFA_ANYCAST: case IFA_UNSPEC: case IFA_CACHEINFO: default: break; } } if(is_link_local) continue; /* link local addresses are not bindable */ if(strlen(ifaces[index].name)==0) strncpy(ifaces[index].name, name, MAX_IF_LEN); ifaces[index].index = index; if(ifaces[index].addresses == 0 ) ifaces[index].addresses = entry; else { for(tmp = ifaces[index].addresses; tmp->next ; tmp = tmp->next)/*empty*/; tmp->next = entry; } } } if(nl_sock>0) close(nl_sock); /* the socket should be closed so we can bind again */ for(i = 0; i < sizeof(families)/sizeof(int); i++){ /* get device flags */ get_flags(families[i]); /* AF_INET or AF_INET6 */ } return 0; error: if(nl_sock>0) close(nl_sock); return -1; } /* add all family type addresses of interface if_to the socket_info array * if if_name==0, adds all addresses on all interfaces * uses RTNETLINK sockets to get addresses on the present interface on LINUX * return: -1 on error, 0 on success */ int add_interfaces_via_netlink(char* if_name, int family, unsigned short port, unsigned short proto, struct addr_info** ai_l) { int i; struct idx* tmp; enum si_flags flags; if(ifaces == NULL && (build_iface_list()!=0)){ LM_ERR("Could not get network interface list\n"); return -1; } flags=SI_NONE; for(i=0; i< MAX_IFACE_NO; ++i){ if(ifaces[i].addresses == NULL) continue; /* not present/configured */ if ((if_name==0)|| (strncmp(if_name, ifaces[i].name, strlen(ifaces[i].name))==0)){ /* check if iface is up */ //if(! (ifaces[i].flags & IFF_UP) ) continue; for(tmp = ifaces[i].addresses; tmp; tmp = tmp->next){ LM_DBG("\t in add_iface_via_netlink Name %s Address %s\n", ifaces[i].name, tmp->addr); /* match family */ if (family == tmp->family){ /* check if loopback */ if (ifaces[i].flags & IFF_LOOPBACK){ LM_DBG("INTERFACE %s is loopback", ifaces[i].name); flags|=SI_IS_LO; } /* save the info */ if (new_addr_info2list(tmp->addr, flags, ai_l)!=0){ LOG(L_ERR, "ERROR: add_interfaces: " "new_addr_info2list failed\n"); goto error; } } } } } return 0; error: return -1; } #endif /* __OS_linux */ /* 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, 0, 0, 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, 0, 0, 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, si->useinfo.name.s, si->useinfo.port_no, 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_valid_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_valid_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/ipv6 interfaces */ if ( ( (add_interfaces(0, AF_INET, 0, PROTO_UDP, &ai_lst)==0) #ifdef USE_IPV6 #ifdef __OS_linux && (!auto_bind_ipv6 || add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_UDP, &ai_lst) == 0) #else && ( !auto_bind_ipv6 || add_interfaces(0, AF_INET6, 0, PROTO_UDP, &ai_lst) !=0 ) /* add_interface does not work for IPv6 on Linux */ #endif /* __OS_linux */ #endif /* USE_IPV6 */ ) && (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) #ifdef USE_IPV6 #ifdef __OS_linux || (auto_bind_ipv6 && add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_TCP, &ai_lst) != 0) #else || (auto_bind_ipv6 && add_interfaces(0, AF_INET6, 0, PROTO_TCP, &ai_lst) !=0 ) #endif /* __OS_linux */ #endif /* USE_IPV6 */ ) || (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) #ifdef USE_IPV6 #ifdef __OS_linux || (auto_bind_ipv6 && add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_TLS, &ai_lst) != 0) #else || (auto_bind_ipv6 && add_interfaces(0, AF_INET6, 0, PROTO_TLS, &ai_lst)!=0) #endif /* __OS_linux */ #endif /* USE_IPV6 */ ) || (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) #ifdef USE_IPV6 #ifdef __OS_linux || (auto_bind_ipv6 && add_interfaces_via_netlink(0, AF_INET6, 0, PROTO_SCTP, &ai_lst) != 0) #else || (auto_bind_ipv6 && add_interfaces(0, AF_INET6, 0, PROTO_SCTP, &ai_lst) != 0) #endif /* __OS_linux */ #endif /* USE_IPV6 */ ) || (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_valid_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_valid_proto_name(proto), si->name.s); if (!si->flags & SI_IS_IP) printf(" [%s]", si->address_str.s); printf( ":%s%s%s", si->port_no_str.s, si->flags & SI_IS_MCAST ? " mcast" : "", si->flags & SI_IS_MHOMED? " mhomed" : ""); if (si->useinfo.name.s) printf(" advertise %s", si->useinfo.name.s); printf("\n"); } } }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_valid_proto_name(a->proto), a->alias.len, a->alias.s, a->port); else printf(" %s: %.*s:*\n", get_valid_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]; } }