mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
7.0 KiB
268 lines
7.0 KiB
/*
|
|
* Copyright (C) 2006 iptelorg GmbH
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio 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
|
|
*
|
|
* Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "init_socks.h"
|
|
#include "../../dprint.h"
|
|
#include "../../ip_addr.h"
|
|
#include "../../resolve.h"
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h> /*IPTOS_LOWDELAY*/
|
|
#include <netinet/tcp.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h> /* unlink */
|
|
#include <sys/stat.h> /* chmod */
|
|
#include <fcntl.h>
|
|
|
|
#ifndef UNIX_PATH_MAX
|
|
#define UNIX_PATH_MAX 104
|
|
#endif
|
|
|
|
|
|
static int tcp_proto_no=-1; /* tcp protocol number as returned by
|
|
getprotobyname */
|
|
|
|
|
|
/* returns -1 on error */
|
|
static int set_non_blocking(int s)
|
|
{
|
|
int flags;
|
|
/* non-blocking */
|
|
flags=fcntl(s, F_GETFL);
|
|
if (flags==-1){
|
|
LOG(L_ERR, "ERROR: set_non_blocking: fnctl failed: (%d) %s\n",
|
|
errno, strerror(errno));
|
|
goto error;
|
|
}
|
|
if (fcntl(s, F_SETFL, flags|O_NONBLOCK)==-1){
|
|
LOG(L_ERR, "ERROR: set_non_blocking: fcntl: set non-blocking failed:"
|
|
" (%d) %s\n", errno, strerror(errno));
|
|
goto error;
|
|
}
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* opens, binds and listens-on a control unix socket of type 'type'
|
|
* it will change the permissions to perm, if perm!=0
|
|
* and the ownership to uid.gid if !=-1
|
|
* returns socket fd or -1 on error */
|
|
int init_unix_sock(struct sockaddr_un* su, char* name, int type, int perm,
|
|
int uid, int gid)
|
|
{
|
|
struct sockaddr_un ifsun;
|
|
int s;
|
|
int len;
|
|
int optval;
|
|
|
|
s=-1;
|
|
unlink(name);
|
|
memset(&ifsun, 0, sizeof (struct sockaddr_un));
|
|
len=strlen(name);
|
|
if (len>UNIX_PATH_MAX){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: name too long (%d > %d): %s\n",
|
|
len, UNIX_PATH_MAX, name);
|
|
goto error;
|
|
}
|
|
ifsun.sun_family=AF_UNIX;
|
|
memcpy(ifsun.sun_path, name, len);
|
|
#ifdef HAVE_SOCKADDR_SA_LEN
|
|
ifsun.sun_len=len;
|
|
#endif
|
|
s=socket(PF_UNIX, type, 0);
|
|
if (s==-1){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: cannot create unix socket %s:"
|
|
" %s [%d]\n", name, strerror(errno), errno);
|
|
goto error;
|
|
}
|
|
optval=1;
|
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))==-1){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: setsockopt: %s [%d]\n",
|
|
strerror(errno), errno);
|
|
/* continue */
|
|
}
|
|
if (set_non_blocking(s)==-1){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: set non blocking failed\n");
|
|
}
|
|
if (bind(s, (struct sockaddr *)&ifsun, sizeof(ifsun))==-1){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: bind: %s [%d]\n",
|
|
strerror(errno), errno);
|
|
goto error;
|
|
}
|
|
/* then the permissions */
|
|
if (perm){ /* mode==0 doesn't make sense, nobody can read/write */
|
|
if (chmod(name, perm)<0){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: failed to change the"
|
|
" permissions for %s to %04o: %s[%d]\n",
|
|
name, perm, strerror(errno), errno);
|
|
goto error;
|
|
}
|
|
}
|
|
/* try to change ownership */
|
|
if ((uid!=-1) && (gid!=-1)){
|
|
if (chown(name, uid, gid)<0){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: failed to change the"
|
|
" owner/group for %s to %d.%d: %s[%d]\n",
|
|
name, uid, gid, strerror(errno), errno);
|
|
goto error;
|
|
}
|
|
}
|
|
if ((type==SOCK_STREAM) && (listen(s, 128)==-1)){
|
|
LOG(L_ERR, "ERROR: init_unix_sock: listen: %s [%d]\n",
|
|
strerror(errno), errno);
|
|
goto error;
|
|
}
|
|
*su=ifsun;
|
|
return s;
|
|
error:
|
|
if (s!=-1) close(s);
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
/* set all socket/fd options: disable nagle, tos lowdelay, non-blocking
|
|
* return -1 on error */
|
|
int init_sock_opt(int s, enum socket_protos type)
|
|
{
|
|
int optval;
|
|
#ifdef DISABLE_NAGLE
|
|
int flags;
|
|
struct protoent* pe;
|
|
#endif
|
|
|
|
if ((type==UDP_SOCK)||(type==TCP_SOCK)){
|
|
#ifdef DISABLE_NAGLE
|
|
if (type==TCP_SOCK){
|
|
flags=1;
|
|
if (tcp_proto_no==-1){ /* if not already set */
|
|
pe=getprotobyname("tcp");
|
|
if (pe!=0){
|
|
tcp_proto_no=pe->p_proto;
|
|
}
|
|
}
|
|
if ( (tcp_proto_no!=-1) && (setsockopt(s, tcp_proto_no,
|
|
TCP_NODELAY, &flags, sizeof(flags))<0) ){
|
|
LOG(L_WARN, "WARNING: init_sock_opt: could not disable"
|
|
" Nagle: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
#endif
|
|
/* tos*/
|
|
optval = IPTOS_LOWDELAY;
|
|
if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*)&optval,
|
|
sizeof(optval)) ==-1){
|
|
LOG(L_WARN, "WARNING: init_sock_opt: setsockopt tos: %s\n",
|
|
strerror(errno));
|
|
/* continue since this is not critical */
|
|
}
|
|
}
|
|
if (set_non_blocking(s)==-1){
|
|
LOG(L_ERR, "ERROR: init_sock_opt: set non blocking failed\n");
|
|
}
|
|
return 0;
|
|
}
|