struct tcp_connection* tcpconn_connect(union sockaddr_union* server, int type) { int s; struct socket_info* si; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; struct ip_addr ip; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LOG(L_ERR, "ERROR: tcpconn_connect: socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (init_sock_opt(s)<0){ LOG(L_ERR, "ERROR: tcpconn_connect: init_sock_opt failed\n"); goto error; } if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){ LOG(L_ERR, "ERROR: tcpconn_connect: tcp_blocking_connect failed\n"); goto error; } my_name_len=sizeof(my_name); if (getsockname(s, &my_name.s, &my_name_len)!=0){ LOG(L_ERR, "ERROR: tcp_connect: getsockname failed: %s(%d)\n", strerror(errno), errno); si=0; /* try to go on */ } su2ip_addr(&ip, &my_name); #ifdef USE_TLS if (type==PROTO_TLS) si=find_si(&ip, 0, PROTO_TLS); else #endif si=find_si(&ip, 0, PROTO_TCP); if (si==0){ LOG(L_ERR, "ERROR: tcp_connect: could not find corresponding" " listening socket, using default...\n"); if (server->s.sa_family==AF_INET) si=sendipv4_tcp; #ifdef USE_IPV6 else si=sendipv6_tcp; #endif } con=tcpconn_new(s, server, si, type, S_CONN_CONNECT); if (con==0){ LOG(L_ERR, "ERROR: tcp_connect: tcpconn_new failed, closing the " " socket\n"); goto error; } return con; /*FIXME: set sock idx! */ error: if (s!=-1) close(s); /* close the opened socket */ return 0; }
/** * 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; }
static struct tcp_connection* ws_sync_connect(struct socket_info* send_sock, union sockaddr_union* server) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con=tcp_conn_new(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } /* it is safe to move this here and clear it after we complete the * handshake, just before sending the fd to main */ con->fd = s; return con; error: /* close the opened socket */ if (s!=-1) close(s); return 0; }
static struct tcp_connection* hep_sync_connect(struct socket_info* send_sock, union sockaddr_union* server, int *fd) { int s; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; s=socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (s==-1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); goto error; } if (tcp_init_sock_opt(s)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(s, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } if (tcp_connect_blocking(s, &server->s, sockaddru_len(*server))<0){ LM_ERR("tcp_blocking_connect failed\n"); goto error; } con = tcp_conn_create(s, server, send_sock, S_CONN_OK); if (con==NULL){ LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *fd = s; return con; /*FIXME: set sock idx! */ error: /* close the opened socket */ if (s!=-1) close(s); return 0; }
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; }
static int tcpconn_async_connect(struct socket_info* send_sock, union sockaddr_union* server, char *buf, unsigned len, struct tcp_connection** c, int *ret_fd) { int fd, n; union sockaddr_union my_name; socklen_t my_name_len; struct tcp_connection* con; struct pollfd pf; unsigned int elapsed,to; int err; unsigned int err_len; int poll_err; char *ip; unsigned short port; struct timeval begin; /* create the socket */ fd = socket(AF2PF(server->s.sa_family), SOCK_STREAM, 0); if (fd == -1){ LM_ERR("socket: (%d) %s\n", errno, strerror(errno)); return -1; } if (tcp_init_sock_opt(fd)<0){ LM_ERR("tcp_init_sock_opt failed\n"); goto error; } my_name_len = sockaddru_len(send_sock->su); memcpy( &my_name, &send_sock->su, my_name_len); su_setport( &my_name, 0); if (bind(fd, &my_name.s, my_name_len )!=0) { LM_ERR("bind failed (%d) %s\n", errno,strerror(errno)); goto error; } /* attempt to do connect and see if we do block or not */ poll_err = 0; elapsed = 0; to = hep_async_local_connect_timeout*1000; if (gettimeofday(&(begin), NULL)) { LM_ERR("Failed to get TCP connect start time\n"); goto error; } again: n = connect(fd, &server->s, sockaddru_len(*server)); if (n == -1) { if (errno == EINTR){ elapsed=get_time_diff(&begin); if (elapsed < to) goto again; else { LM_DBG("Local connect attempt failed \n"); goto async_connect; } } if (errno != EINPROGRESS && errno!=EALREADY) { get_su_info(&server->s, ip, port); LM_ERR("[server=%s:%d] (%d) %s\n",ip, port, errno,strerror(errno)); goto error; } } else goto local_connect; /* let's poll for a little */ pf.fd = fd; pf.events = POLLOUT; while(1){ elapsed = get_time_diff(&begin); if (elapsed < to) to -= elapsed; else { LM_DBG("Polling is overdue \n"); goto async_connect; } n = poll(&pf, 1, to/1000); if (n < 0){ if (errno == EINTR) continue; get_su_info(&server->s, ip, port); LM_ERR("poll/select failed:[server=%s:%d] (%d) %s\n", ip, port, errno, strerror(errno)); goto error; } else if (n==0) /* timeout */ continue; if (pf.revents & (POLLERR|POLLHUP|POLLNVAL)){ LM_ERR("poll error: flags %x\n", pf.revents); poll_err=1; } err_len=sizeof(err); getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &err_len); if ((err==0) && (poll_err==0)) goto local_connect; if (err!=EINPROGRESS && err!=EALREADY){ get_su_info(&server->s, ip, port); LM_ERR("failed to retrieve SO_ERROR [server=%s:%d] (%d) %s\n", ip, port, err, strerror(err)); goto error; } } async_connect: LM_DBG("Create connection for async connect\n"); /* create a new dummy connection */ con = tcp_conn_create(fd, server, send_sock, S_CONN_CONNECTING); if (con==NULL) { LM_ERR("tcp_conn_create failed\n"); goto error; } /* attach the write buffer to it */ lock_get(&con->write_lock); if (add_write_chunk(con,buf,len,0) < 0) { LM_ERR("Failed to add the initial write chunk\n"); /* FIXME - seems no more SHM now ... * continue the async connect process ? */ } lock_release(&con->write_lock); /* report an async, in progress connect */ *c = con; return 0; local_connect: con = tcp_conn_create(fd, server, send_sock, S_CONN_OK); if (con==NULL) { LM_ERR("tcp_conn_create failed, closing the socket\n"); goto error; } *c = con; *ret_fd = fd; /* report a local connect */ return 1; error: close(fd); *c = NULL; 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; }
/* opens, binds and listens-on a control tcp socket * returns socket fd or -1 on error */ int init_tcpudp_sock(union sockaddr_union* sa_un, char* address, int port, enum socket_protos type) { union sockaddr_union su; struct hostent* he; int s; int optval; s=-1; if ((type!=UDP_SOCK) && (type!=TCP_SOCK)){ LOG(L_CRIT, "BUG: init_tcpudp_sock called with bad type: %d\n", type); goto error; } memset(&su, 0, sizeof (su)); /* if no address specified, or address=='*', listen on all * ipv4 addresses */ if ((address==0)||((*address)==0)||((*address=='*') && (*(address+1)==0))){ su.sin.sin_family=AF_INET; su.sin.sin_port=htons(port); su.sin.sin_addr.s_addr=INADDR_ANY; #ifdef HAVE_SOCKADDR_SA_LEN su.sin.sin_len=sizeof(struct sockaddr_in); #endif }else{ he=resolvehost(address); if (he==0){ LOG(L_ERR, "ERROR: init_tcpudp_sock: bad address %s\n", address); goto error; } if (hostent2su(&su, he, 0, port)==-1) goto error; } s=socket(AF2PF(su.s.sa_family), (type==TCP_SOCK)?SOCK_STREAM:SOCK_DGRAM,0); if (s==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: cannot create tcp socket:" " %s [%d]\n", strerror(errno), errno); goto error; } /* REUSEADDR */ optval=1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: setsockopt: %s [%d]\n", strerror(errno), errno); /* continue */ } /* tos */ optval=IPTOS_LOWDELAY; if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*)&optval,sizeof(optval)) ==-1){ LOG(L_WARN, "WARNING: init_tcpudp_sock: setsockopt tos: %s\n", strerror(errno)); /* continue since this is not critical */ } if (set_non_blocking(s)==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: set non blocking failed\n"); } if (bind(s, &su.s, sockaddru_len(su))==-1){ LOG(L_ERR, "ERROR: init_tcpudp_sock: bind: %s [%d]\n", strerror(errno), errno); goto error; } if ((type==TCP_SOCK) && (listen(s, 128)==-1)){ LOG(L_ERR, "ERROR: init_tcpudp_sock: listen: %s [%d]\n", strerror(errno), errno); goto error; } *sa_un=su; return s; error: if (s!=-1) close(s); 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; }