/** * opens the server socket, which attends (accepts) the clients, that is: * params: * address: * address to which to listen * port: * base port to which to listen. then port+1 will be the socket * for action's delivery. * fds: * in fd[0] the action socket will be put. * in fd[1] the event socket will be put. * * returns 0 on exit, <0 on fail * */ static int open_server_sockets(struct ip_addr *address,unsigned short port,int *fd) { /*using sockaddr_union enables ipv6..*/ union sockaddr_union su; int i,optval; fd[0]=fd[1]=-1; if(address->af!=AF_INET && address->af!=AF_INET6){ LM_ERR("Only ip and ipv6 allowed socket types\n"); return -1; } for(i=0;i<2;i++){ if(init_su(&su,address,port+i)<0){ LM_ERR("unable to init sockaddr_union\n"); return -1; } if((fd[i]=socket(AF2PF(su.s.sa_family), SOCK_STREAM, 0))==-1){ LM_ERR("trying to open server %s socket (%s)\n",i==0?"event":"action",strerror(errno)); goto error; } optval=1; if (setsockopt(fd[i], SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LM_ERR("setsockopt (%s)\n",strerror(errno)); goto error; } if ((bind(fd[i], &su.s,sizeof(union sockaddr_union)))==-1){ LM_ERR( "bind (%s)\n",strerror(errno)); goto error; } if (listen(fd[i], 10)==-1){ LM_ERR( "listen (%s)\n",strerror(errno)); goto error; } } return 0; error: for(i=0;i<2;i++) if(fd[i]!=-1){ close(fd[i]); fd[i]=-1; } return -1; }
int tcp_init(struct socket_info* sock_info) { union sockaddr_union* addr; int optval; #ifdef DISABLE_NAGLE int flag; struct protoent* pe; if (tcp_proto_no==-1){ /* if not already set */ pe=getprotobyname("tcp"); if (pe==0){ LOG(L_ERR, "ERROR: tcp_init: could not get TCP protocol number\n"); tcp_proto_no=-1; }else{ tcp_proto_no=pe->p_proto; } } #endif addr=&sock_info->su; sock_info->proto=PROTO_TCP; if (init_su(addr, &sock_info->address, sock_info->port_no)<0){ LOG(L_ERR, "ERROR: tcp_init: could no init sockaddr_union\n"); goto error; } sock_info->socket=socket(AF2PF(addr->s.sa_family), SOCK_STREAM, 0); if (sock_info->socket==-1){ LOG(L_ERR, "ERROR: tcp_init: socket: %s\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE flag=1; if ( (tcp_proto_no!=-1) && (setsockopt(sock_info->socket, tcp_proto_no , TCP_NODELAY, &flag, sizeof(flag))<0) ){ LOG(L_ERR, "ERROR: tcp_init: could not disable Nagle: %s\n", strerror(errno)); } #endif #if !defined(TCP_DONT_REUSEADDR) /* Stevens, "Network Programming", Section 7.5, "Generic Socket * Options": "...server started,..a child continues..on existing * connection..listening server is restarted...call to bind fails * ... ALL TCP servers should specify the SO_REUSEADDRE option * to allow the server to be restarted in this situation * * Indeed, without this option, the server can't restart. * -jiri */ optval=1; if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LOG(L_ERR, "ERROR: tcp_init: setsockopt %s\n", strerror(errno)); goto error; } #endif /* tos */ optval=IPTOS_LOWDELAY; if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LOG(L_WARN, "WARNING: tcp_init: setsockopt tos: %s\n", strerror(errno)); /* continue since this is not critical */ } if (bind(sock_info->socket, &addr->s, sockaddru_len(*addr))==-1){ LOG(L_ERR, "ERROR: tcp_init: bind(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); goto error; } if (listen(sock_info->socket, 10)==-1){ LOG(L_ERR, "ERROR: tcp_init: listen(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); goto error; } return 0; error: if (sock_info->socket!=-1){ close(sock_info->socket); sock_info->socket=-1; } return -1; }
/** * Evaluate if next hop is a strict or loose router, by looking at the * retr. buffer of the original INVITE. * Assumes: * orig_inv is a parsed SIP message; * rtset is not NULL. * @return: * F_RB_NH_LOOSE : next hop was loose router; * F_RB_NH_STRICT: nh is strict; * 0 on error. */ static unsigned long nhop_type(sip_msg_t *orig_inv, rte_t *rtset, const struct dest_info *dst_inv, str *contact) { struct sip_uri puri, topr_uri, lastr_uri, inv_ruri, cont_uri; struct ip_addr *uri_ia; union sockaddr_union uri_sau; unsigned int uri_port, dst_port, inv_port, cont_port, lastr_port; rte_t *last_r; #ifdef TM_LOC_ACK_DO_REV_DNS struct ip_addr ia; struct hostent *he; char **alias; #endif #define PARSE_URI(_str_, _uri_) \ do { \ /* parse_uri() 0z the puri */ \ if (parse_uri((_str_)->s, \ (_str_)->len, _uri_) < 0) { \ LM_ERR("failed to parse route body '%.*s'.\n", STR_FMT(_str_)); \ return 0; \ } \ } while (0) #define HAS_LR(_rte_) \ ({ \ PARSE_URI(&(_rte_)->ptr->nameaddr.uri, &puri); \ puri.lr.s; \ }) #define URI_PORT(_puri_, _port) \ do { \ if (! (_port = uri2port(_puri_))) \ return 0; \ } while (0) /* examine the easy/fast & positive cases foremost */ /* [1] check if 1st route lacks ;lr */ LM_DBG("checking lack of ';lr' in 1st route.\n"); if (! HAS_LR(rtset)) return F_RB_NH_STRICT; topr_uri = puri; /* save 1st route's URI */ /* [2] check if last route shows ;lr */ LM_DBG("checking presence of ';lr' in last route.\n"); for (last_r = rtset; last_r->next; last_r = last_r->next) /* scroll down to last route */ ; if (HAS_LR(last_r)) return F_RB_NH_LOOSE; /* [3] 1st route has ;lr -> check if the destination of original INV * equals the address provided by this route; if does -> loose */ LM_DBG("checking INVITE's destination against its first route.\n"); URI_PORT(&topr_uri, uri_port); if (! (dst_port = su_getport(&dst_inv->to))) return 0; /* not really expected */ if (dst_port != uri_port) return F_RB_NH_STRICT; /* if 1st route contains an IP address, comparing it against .dst */ if ((uri_ia = str2ip(&topr_uri.host)) || (uri_ia = str2ip6(&topr_uri.host)) ) { /* we have an IP address in route -> comparison can go swiftly */ if (init_su(&uri_sau, uri_ia, uri_port) < 0) return 0; /* not really expected */ if (su_cmp(&uri_sau, &dst_inv->to)) /* ;lr and sent there */ return F_RB_NH_LOOSE; else /* ;lr and NOT sent there (probably sent to RURI address) */ return F_RB_NH_STRICT; } else { /*if 1st route contains a name, rev resolve the .dst and compare*/ LM_INFO("Failed to decode string '%.*s' in route set element as IP" " address. Trying name resolution.\n",STR_FMT(&topr_uri.host)); /* TODO: alternatively, rev name and compare against dest. IP. */ #ifdef TM_LOC_ACK_DO_REV_DNS ia.af = 0; su2ip_addr(&ia, (void *)&dst_inv->to); if (! ia.af) return 0; /* not really expected */ if ((he = rev_resolvehost(&ia))) { if ((strlen(he->h_name) == topr_uri.host.len) && (memcmp(he->h_name, topr_uri.host.s, topr_uri.host.len) == 0)) return F_RB_NH_LOOSE; for (alias = he->h_aliases; *alias; alias ++) if ((strlen(*alias) == topr_uri.host.len) && (memcmp(*alias, topr_uri.host.s, topr_uri.host.len) == 0)) return F_RB_NH_LOOSE; return F_RB_NH_STRICT; } else { LM_INFO("failed to resolve address '%s' to a name.\n", ip_addr2a(&ia)); } #endif } LM_WARN("failed to establish with certainty the type of next hop;" " trying an educated guess.\n"); /* [4] compare (possibly updated) remote target to original RURI; if * equal, a strict router's address wasn't filled in as RURI -> loose */ LM_DBG("checking remote target against INVITE's RURI.\n"); PARSE_URI(contact, &cont_uri); PARSE_URI(GET_RURI(orig_inv), &inv_ruri); URI_PORT(&cont_uri, cont_port); URI_PORT(&inv_ruri, inv_port); if ((cont_port == inv_port) && (cont_uri.host.len == inv_ruri.host.len) && (memcmp(cont_uri.host.s, inv_ruri.host.s, cont_uri.host.len) == 0)) return F_RB_NH_LOOSE; /* [5] compare (possibly updated) remote target to last route; if equal, * strict router's address might have been filled as RURI and remote * target appended to route set -> strict */ LM_DBG("checking remote target against INVITE's last route.\n"); PARSE_URI(&last_r->ptr->nameaddr.uri, &lastr_uri); URI_PORT(&lastr_uri, lastr_port); if ((cont_port == lastr_port) && (cont_uri.host.len == lastr_uri.host.len) && (memcmp(cont_uri.host.s, lastr_uri.host.s, lastr_uri.host.len) == 0)) return F_RB_NH_STRICT; LM_WARN("failed to establish the type of next hop;" " assuming loose router.\n"); return F_RB_NH_LOOSE; #undef PARSE_URI #undef HAS_LR #undef URI_PORT }
/** create and return a raw socket. * @param proto - protocol used (e.g. IPPROTO_UDP, IPPROTO_RAW) * @param ip - if not null the socket will be bound on this ip. * @param iface - if not null the socket will be bound to this interface * (SO_BINDTODEVICE). This is supported only on linux. * @param iphdr_incl - set to 1 if packets send on this socket include * a pre-built ip header (some fields, like the checksum * will still be filled by the kernel, OTOH packet * fragmentation has to be done in user space). * @return socket on success, -1 on error */ int raw_socket(int proto, struct ip_addr* ip, str* iface, int iphdr_incl) { int sock; int t; union sockaddr_union su; #if defined (SO_BINDTODEVICE) char short_ifname[sizeof(int)]; int ifname_len; char* ifname; #endif /* SO_BINDTODEVICE */ sock = socket(PF_INET, SOCK_RAW, proto); if (sock==-1) goto error; /* set socket options */ if (iphdr_incl) { t=1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &t, sizeof(t))<0){ ERR("raw_socket: setsockopt(IP_HDRINCL) failed: %s [%d]\n", strerror(errno), errno); goto error; } } else { /* IP_PKTINFO makes no sense if the ip header is included */ /* using IP_PKTINFO */ t=1; #ifdef IP_PKTINFO if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &t, sizeof(t))<0){ ERR("raw_socket: setsockopt(IP_PKTINFO) failed: %s [%d]\n", strerror(errno), errno); goto error; } #elif defined(IP_RECVDSTADDR) if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &t, sizeof(t))<0){ ERR("raw_socket: setsockop(IP_RECVDSTADDR) failed: %s [%d]\n", strerror(errno), errno); goto error; } #else #error "no method of getting the destination ip address supported" #endif /* IP_RECVDSTADDR / IP_PKTINFO */ } #if defined (IP_MTU_DISCOVER) && defined (IP_PMTUDISC_DONT) t=IP_PMTUDISC_DONT; if(setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &t, sizeof(t)) ==-1){ ERR("raw_socket: setsockopt(IP_MTU_DISCOVER): %s\n", strerror(errno)); goto error; } #endif /* IP_MTU_DISCOVER && IP_PMTUDISC_DONT */ if (iface && iface->s){ #if defined (SO_BINDTODEVICE) /* workaround for linux bug: arg to setsockopt must have at least * sizeof(int) size or EINVAL would be returned */ if (iface->len<sizeof(int)){ memcpy(short_ifname, iface->s, iface->len); short_ifname[iface->len]=0; /* make sure it's zero term */ ifname_len=sizeof(short_ifname); ifname=short_ifname; }else{ ifname_len=iface->len; ifname=iface->s; } if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, ifname_len) <0){ ERR("raw_socket: could not bind to %.*s: %s [%d]\n", iface->len, ZSW(iface->s), strerror(errno), errno); goto error; } #else /* !SO_BINDTODEVICE */ /* SO_BINDTODEVICE is linux specific => cannot bind to a device */ ERR("raw_socket: bind to device supported only on linux\n"); goto error; #endif /* SO_BINDTODEVICE */ } /* FIXME: probe_max_receive_buffer(sock) missing */ if (ip){ init_su(&su, ip, 0); if (bind(sock, &su.s, sockaddru_len(su))==-1){ ERR("raw_socket: bind(%s) failed: %s [%d]\n", ip_addr2a(ip), strerror(errno), errno); goto error; } } return sock; error: if (sock!=-1) close(sock); return -1; }
/* initializes an already defined TCP listener */ int tcp_init_listener(struct socket_info *si) { union sockaddr_union* addr; int optval; #ifdef DISABLE_NAGLE int flag; struct protoent* pe; if (tcp_proto_no==-1){ /* if not already set */ pe=getprotobyname("tcp"); if (pe==0){ LM_ERR("could not get TCP protocol number\n"); tcp_proto_no=-1; }else{ tcp_proto_no=pe->p_proto; } } #endif addr = &si->su; if (init_su(addr, &si->address, si->port_no)<0){ LM_ERR("could no init sockaddr_union\n"); goto error; } si->socket = socket(AF2PF(addr->s.sa_family), SOCK_STREAM, 0); if (si->socket==-1){ LM_ERR("socket failed with [%s]\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE flag=1; if ( (tcp_proto_no!=-1) && (setsockopt(si->socket, tcp_proto_no , TCP_NODELAY, &flag, sizeof(flag))<0) ){ LM_ERR("could not disable Nagle: %s\n",strerror(errno)); } #endif #if !defined(TCP_DONT_REUSEADDR) /* Stevens, "Network Programming", Section 7.5, "Generic Socket * Options": "...server started,..a child continues..on existing * connection..listening server is restarted...call to bind fails * ... ALL TCP servers should specify the SO_REUSEADDRE option * to allow the server to be restarted in this situation */ optval=1; if (setsockopt(si->socket, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval))==-1) { LM_ERR("setsockopt failed with [%s]\n", strerror(errno)); goto error; } #endif /* tos */ optval = tos; if (setsockopt(si->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LM_WARN("setsockopt tos: %s\n", strerror(errno)); /* continue since this is not critical */ } if (probe_max_sock_buff(si->socket,1,MAX_SEND_BUFFER_SIZE, BUFFER_INCREMENT)) { LM_WARN("setsockopt tcp snd buff: %s\n",strerror(errno)); /* continue since this is not critical */ } init_sock_keepalive(si->socket); if (bind(si->socket, &addr->s, sockaddru_len(*addr))==-1){ LM_ERR("bind(%x, %p, %d) on %s:%d : %s\n", si->socket, &addr->s, (unsigned)sockaddru_len(*addr), si->address_str.s, si->port_no, strerror(errno)); goto error; } if (listen(si->socket, tcp_listen_backlog)==-1){ LM_ERR("listen(%x, %p, %d) on %s: %s\n", si->socket, &addr->s, (unsigned)sockaddru_len(*addr), si->address_str.s, strerror(errno)); goto error; } return 0; error: if (si->socket!=-1){ close(si->socket); si->socket=-1; } return -1; }
int sctp_server_init(struct socket_info* sock_info) { union sockaddr_union* addr; int optval; addr=&sock_info->su; sock_info->proto=PROTO_SCTP; if (init_su(addr, &sock_info->address, sock_info->port_no)<0){ LM_ERR("could not init sockaddr_union\n"); goto error; } sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_SEQPACKET, 0); if (sock_info->socket==-1){ LM_ERR("socket: %s [%d]\n", strerror(errno), errno); goto error; } optval=1; if (setsockopt(sock_info->socket, SOL_SOCKET, SO_REUSEADDR , (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("setsockopt: %s\n", strerror(errno)); goto error; } #ifdef DISABLE_NAGLE /* turns of Nagle-like algorithm/chunk-bundling.*/ optval=1; if (setsockopt(sock_info->socket, IPPROTO_SCTP, SCTP_NODELAY, (void*)&optval, sizeof(optval))==-1){ LM_WARN("setsockopt %s\n", strerror(errno)); /* continues since this is not critical */ } #endif /* tos */ /* this sockopt causes a kernel panic in some sctp implementations. * commenting it out. -gmarmon */ /* optval=tos; if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) ==-1){ LM_WARN("setsockopt tos: %s\n", strerror(errno)); } */ #if defined (__linux__) && defined(SCTP_ERRORS) /* will SCTP_ERRORS ever be defined? -gmarmon */ optval=1; /* enable error receiving on unconnected sockets */ if(setsockopt(sock_info->socket, SOL_IP, IP_RECVERR, (void*)&optval, sizeof(optval)) ==-1){ LM_ERR("setsockopt: %s\n", strerror(errno)); goto error; } #endif /*if ( probe_max_receive_buffer(sock_info->socket)==-1) goto error; */ if (bind(sock_info->socket, &addr->s, sockaddru_len(*addr))==-1){ LM_ERR("bind(%x, %p, %d) on %s: %s\n", sock_info->socket, &addr->s, (unsigned)sockaddru_len(*addr), sock_info->address_str.s, strerror(errno)); #ifdef USE_IPV6 if (addr->s.sa_family==AF_INET6) LM_ERR("might be caused by using a link " " local address, try site local or global\n"); #endif goto error; } if(listen(sock_info->socket, LISTEN_BACKLOG)<0){ LM_ERR("listen(%x, %d) on %s: %s\n", sock_info->socket, LISTEN_BACKLOG, sock_info->address_str.s, strerror(errno)); goto error; } /* pkg_free(addr);*/ return 0; error: /* if (addr) pkg_free(addr);*/ return -1; }