- TCP support
This commit is contained in:
62
src/proxy.c
62
src/proxy.c
@@ -95,6 +95,16 @@ int proxy_request (sip_ticket_t *ticket) {
|
||||
|
||||
request=ticket->sipmsg;
|
||||
|
||||
/*
|
||||
* RFC&&&&
|
||||
* add a received= parameter to the topmost Via header. Used for TCP
|
||||
* connections - send answer within the existing TCP connection back
|
||||
* to client.
|
||||
*/
|
||||
if (ticket->protocol == PROTO_TCP) {
|
||||
sip_add_received_param(ticket);
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 3261, Section 16.4
|
||||
* Proxy Behavior - Route Information Preprocessing
|
||||
@@ -337,29 +347,6 @@ sts=sip_obscure_callid(ticket);
|
||||
* 2) fixed outbound proxy
|
||||
* 3) SIP URI
|
||||
*/
|
||||
/*
|
||||
* fixed or domain outbound proxy defined ?
|
||||
*/
|
||||
// let's try with Route header first
|
||||
#if 0
|
||||
if ((type == REQTYP_OUTGOING) &&
|
||||
(sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_request: have outbound proxy %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
/*
|
||||
* Route present?
|
||||
* If so, fetch address from topmost Route: header and remove it.
|
||||
*/
|
||||
} else if ((type == REQTYP_OUTGOING) &&
|
||||
(!osip_list_eol(&(request->routes), 0))) {
|
||||
sts=route_determine_nexthop(ticket, &sendto_addr, &port);
|
||||
if (sts == STS_FAILURE) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_request: route_determine_nexthop failed");
|
||||
return STS_FAILURE;
|
||||
}
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_request: have Route header to %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
#else
|
||||
/*
|
||||
* Route present?
|
||||
* If so, fetch address from topmost Route: header and remove it.
|
||||
@@ -373,11 +360,13 @@ sts=sip_obscure_callid(ticket);
|
||||
}
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_request: have Route header to %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
/*
|
||||
* fixed or domain outbound proxy defined ?
|
||||
*/
|
||||
} else if ((type == REQTYP_OUTGOING) &&
|
||||
(sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_request: have outbound proxy %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
#endif
|
||||
/*
|
||||
* destination from SIP URI
|
||||
*/
|
||||
@@ -670,18 +659,17 @@ sts=sip_obscure_callid(ticket);
|
||||
* Determine Next-Hop Address
|
||||
*/
|
||||
/*&&&& priority probably should be:
|
||||
* 0) rport=;received= header (TCP only for now)
|
||||
* 1) Route header
|
||||
* 2) fixed outbound proxy
|
||||
* 3) Via header
|
||||
*/
|
||||
/*
|
||||
* check if we need to send to an outbound proxy
|
||||
* IF TCP, check for rport=x;received=y parameters in VIA
|
||||
*/
|
||||
// let's try with Route header first
|
||||
#if 0
|
||||
if ((type == RESTYP_OUTGOING) &&
|
||||
(sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_response: have outbound proxy %s:%i",
|
||||
if ((ticket->protocol == PROTO_TCP) &&
|
||||
(sip_get_received_param(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_response: have received/rport to %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
/*
|
||||
* Route present?
|
||||
@@ -696,25 +684,13 @@ sts=sip_obscure_callid(ticket);
|
||||
}
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_response: have Route header to %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
#else
|
||||
/*
|
||||
* Route present?
|
||||
* If so, fetch address from topmost Route: header and remove it.
|
||||
* check if we need to send to an outbound proxy
|
||||
*/
|
||||
if ((type == RESTYP_OUTGOING) &&
|
||||
(!osip_list_eol(&(response->routes), 0))) {
|
||||
sts=route_determine_nexthop(ticket, &sendto_addr, &port);
|
||||
if (sts == STS_FAILURE) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_response: route_determine_nexthop failed");
|
||||
return STS_FAILURE;
|
||||
}
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_response: have Route header to %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
} else if ((type == RESTYP_OUTGOING) &&
|
||||
(sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
|
||||
DEBUGC(DBCLASS_PROXY, "proxy_response: have outbound proxy %s:%i",
|
||||
utils_inet_ntoa(sendto_addr), port);
|
||||
#endif
|
||||
} else {
|
||||
/* get target address and port from VIA header */
|
||||
via = (osip_via_t *) osip_list_get (&(response->vias), 0);
|
||||
|
||||
@@ -661,10 +661,10 @@ int rtp_relay_start_fwd (osip_call_id_t *callid, client_id_t client_id,
|
||||
/* port is available, try to allocate */
|
||||
if (j == RTPPROXY_SIZE) {
|
||||
port=i;
|
||||
sock=sockbind(local_ipaddr, port, 0); /* RTP */
|
||||
sock=sockbind(local_ipaddr, port, PROTO_UDP, 0); /* RTP */
|
||||
|
||||
if (sock) {
|
||||
sock_con=sockbind(local_ipaddr, port+1, 0); /* RTCP */
|
||||
sock_con=sockbind(local_ipaddr, port+1, PROTO_UDP, 0); /* RTCP */
|
||||
/* if success break, else try further on */
|
||||
if (sock_con) break;
|
||||
sts = close(sock);
|
||||
@@ -908,6 +908,9 @@ int rtp_relay_stop_fwd (osip_call_id_t *callid,
|
||||
rtp_proxytable[i].remote_ipaddr,
|
||||
rtp_proxytable[i].remote_port + 1);
|
||||
/* clean up */
|
||||
if (rtp_proxytable[i].opposite_entry) {
|
||||
rtp_proxytable[rtp_proxytable[i].opposite_entry-1].opposite_entry=0;
|
||||
}
|
||||
memset(&rtp_proxytable[i], 0, sizeof(rtp_proxytable[0]));
|
||||
got_match=1;
|
||||
}
|
||||
@@ -1053,6 +1056,9 @@ static int match_socket (int rtp_proxytable_idx) {
|
||||
strcpy(remip2, utils_inet_ntoa(rtp_proxytable[rtp_proxytable_idx].remote_ipaddr));
|
||||
strcpy(lclip2, utils_inet_ntoa(rtp_proxytable[rtp_proxytable_idx].local_ipaddr));
|
||||
|
||||
rtp_proxytable[rtp_proxytable_idx].opposite_entry=j+1;
|
||||
rtp_proxytable[j].opposite_entry=rtp_proxytable_idx+1;
|
||||
|
||||
DEBUGC(DBCLASS_RTP, "connected entry %i (fd=%i, %s:%i->%s:%i) <-> entry %i (fd=%i, %s:%i->%s:%i)",
|
||||
j, rtp_proxytable[j].rtp_rx_sock,
|
||||
lclip1, rtp_proxytable[j].local_port,
|
||||
|
||||
102
src/sip_utils.c
102
src/sip_utils.c
@@ -578,10 +578,17 @@ int sip_add_myvia (sip_ticket_t *ticket, int interface) {
|
||||
sts = sip_calculate_branch_id(ticket, branch_id);
|
||||
|
||||
myaddr=utils_inet_ntoa(addr);
|
||||
sprintf(tmp, "SIP/2.0/UDP %s:%i;branch=%s%s",
|
||||
myaddr, configuration.sip_listen_port,
|
||||
branch_id,
|
||||
(add_rport)? ";rport":"");
|
||||
if (ticket->protocol == PROTO_UDP) {
|
||||
sprintf(tmp, "SIP/2.0/UDP %s:%i;branch=%s%s",
|
||||
myaddr, configuration.sip_listen_port,
|
||||
branch_id,
|
||||
(add_rport)? ";rport":"");
|
||||
} else {
|
||||
sprintf(tmp, "SIP/2.0/TCP %s:%i;branch=%s%s",
|
||||
myaddr, configuration.sip_listen_port,
|
||||
branch_id,
|
||||
(add_rport)? ";rport":"");
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_BABBLE,"adding VIA:%s",tmp);
|
||||
|
||||
@@ -697,6 +704,11 @@ int sip_rewrite_contact (sip_ticket_t *ticket, int direction) {
|
||||
osip_uri_clone(urlmap[i].true_url, &contact->url);
|
||||
}
|
||||
|
||||
/* add transport=tcp parameter if TCP */
|
||||
if (ticket->protocol == PROTO_TCP) {
|
||||
osip_uri_set_transport_tcp(contact->url);
|
||||
}
|
||||
|
||||
osip_list_add(&(sip_msg->contacts),contact,j);
|
||||
replaced=1;
|
||||
}
|
||||
@@ -1144,7 +1156,7 @@ int sip_find_direction(sip_ticket_t *ticket, int *urlidx) {
|
||||
* Also, my own outbound address is considered to be redirected traffic
|
||||
* Example Scenario:
|
||||
* Softphone(or PBX) running on the same host as siproxd is running.
|
||||
* Using iptables, you do a REDIRECT of outgoping SIP traffix of the
|
||||
* Using iptables, you do a REDIRECT of outgoing SIP traffic of the
|
||||
* PBX to be passed to siproxd.
|
||||
*/
|
||||
if (type == DIRTYP_UNKNOWN) {
|
||||
@@ -1376,3 +1388,83 @@ DEBUGC(DBCLASS_PROXY, "tmp+myidentlen=[%s], FromTag=[%s]",
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SIP_ADD_RECEIVED_PARAM
|
||||
*
|
||||
* Add a received parameter to the topmost VIA header (IP and port)
|
||||
*
|
||||
* RETURNS
|
||||
* STS_SUCCESS on success
|
||||
*/
|
||||
int sip_add_received_param(sip_ticket_t *ticket){
|
||||
osip_via_t *via;
|
||||
char tmp[6];
|
||||
|
||||
DEBUGC(DBCLASS_PROXY,"adding received= param to topmost via");
|
||||
via = osip_list_get (&(ticket->sipmsg->vias), 0);
|
||||
|
||||
/* set rport=xxx;received=1.2.3.4 */
|
||||
snprintf(tmp, sizeof(tmp), "%i", ntohs(ticket->from.sin_port));
|
||||
osip_via_param_add(via,osip_strdup("rport"),osip_strdup(tmp));
|
||||
|
||||
osip_via_param_add(via,osip_strdup("received"),
|
||||
osip_strdup(utils_inet_ntoa(ticket->from.sin_addr)));
|
||||
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SIP_GET_RECEIVED_PARAM
|
||||
*
|
||||
* Get a received parameter from the topmost VIA header (IP and port)
|
||||
*
|
||||
* RETURNS
|
||||
* STS_SUCCESS on success
|
||||
*/
|
||||
int sip_get_received_param(sip_ticket_t *ticket,
|
||||
struct in_addr *dest, int *port) {
|
||||
osip_via_t *via;
|
||||
osip_generic_param_t *received=NULL;
|
||||
osip_generic_param_t *rport=NULL;
|
||||
int sts;
|
||||
|
||||
DEBUGC(DBCLASS_PROXY,"searching received= param in topmost via");
|
||||
via = osip_list_get (&(ticket->sipmsg->vias), 0);
|
||||
|
||||
osip_via_param_get_byname (via, "received", &received);
|
||||
osip_via_param_get_byname (via, "rport", &rport);
|
||||
|
||||
if (received && rport && received->gvalue && rport->gvalue) {
|
||||
/* fetch the IP */
|
||||
sts = get_ip_by_host(received->gvalue, dest);
|
||||
if (sts != STS_SUCCESS) return STS_FAILURE;
|
||||
|
||||
/* fetch the port number */
|
||||
*port = atoi(rport->gvalue);
|
||||
if ((*port <=0) || (*port >=65536)) return STS_FAILURE;
|
||||
|
||||
/* If TCP, then validate first if an existing connection is in the cache.
|
||||
* If not, do not use this - a new conection must be established! */
|
||||
if (ticket->protocol == PROTO_TCP) {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
memcpy(&addr.sin_addr, dest, sizeof(struct in_addr));
|
||||
addr.sin_port= htons(*port);
|
||||
sts = tcp_find(addr);
|
||||
if (sts < 0) {
|
||||
DEBUGC(DBCLASS_BABBLE, "IP: %s, port: %i not found in cache",
|
||||
utils_inet_ntoa(*dest), *port);
|
||||
return STS_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* found, return */
|
||||
DEBUGC(DBCLASS_BABBLE, "IP:%s, port:%i is ok to be reused",
|
||||
utils_inet_ntoa(*dest), *port);
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return STS_FAILURE;
|
||||
}
|
||||
|
||||
@@ -100,21 +100,31 @@ struct siproxd_config {
|
||||
char *plugin_dir;
|
||||
stringa_t load_plugin;
|
||||
int sip_dscp;
|
||||
int tcp_timeout;
|
||||
int tcp_connect_timeout;
|
||||
int tcp_keepalive;
|
||||
};
|
||||
|
||||
/*
|
||||
* control structure for config file parser
|
||||
*/
|
||||
typedef struct {
|
||||
int int4;
|
||||
char *string;
|
||||
} defval_t;
|
||||
typedef struct {
|
||||
char *keyword;
|
||||
enum type {TYP_INT4, TYP_STRING, TYP_FLOAT, TYP_STRINGA} type;
|
||||
void *dest;
|
||||
defval_t defval;
|
||||
} cfgopts_t;
|
||||
|
||||
/*
|
||||
* SIP ticket
|
||||
*/
|
||||
typedef struct {
|
||||
char *raw_buffer; /* raw UDP packet */
|
||||
int raw_buffer_len; /* length of raw data */
|
||||
osip_message_t *sipmsg; /* SIP */
|
||||
struct sockaddr_in from; /* received from */
|
||||
#define PROTO_UNKN -1
|
||||
@@ -151,12 +161,13 @@ typedef struct {
|
||||
|
||||
/* sock.c */
|
||||
int sipsock_listen(void); /*X*/
|
||||
int sipsock_wait(void);
|
||||
int sipsock_read(void *buf, size_t bufsize,
|
||||
struct sockaddr_in *from, int *protocol);
|
||||
//int sipsock_wait(void);
|
||||
int sipsock_waitfordata(char *buf, size_t bufsize,
|
||||
struct sockaddr_in *from, int *protocol);
|
||||
int sipsock_send(struct in_addr addr, int port, int protocol, /*X*/
|
||||
char *buffer, size_t size);
|
||||
int sockbind(struct in_addr ipaddr, int localport, int errflg);
|
||||
int sockbind(struct in_addr ipaddr, int localport, int protocol, int errflg);
|
||||
int tcp_find(struct sockaddr_in dst_addr);
|
||||
|
||||
/* register.c */
|
||||
void register_init(void);
|
||||
@@ -173,7 +184,7 @@ int proxy_rewrite_invitation_body(sip_ticket_t *ticket, int direction); /*X*/
|
||||
int proxy_rewrite_request_uri(osip_message_t *mymsg, int idx); /*X*/
|
||||
int proxy_rewrite_useragent(sip_ticket_t *ticket); /*X*/
|
||||
|
||||
/* route_preprocessing.c */
|
||||
/* route_processing.c */
|
||||
int route_preprocess(sip_ticket_t *ticket); /*X*/
|
||||
int route_add_recordroute(sip_ticket_t *ticket); /*X*/
|
||||
int route_purge_recordroute(sip_ticket_t *ticket); /*X*/
|
||||
@@ -209,10 +220,12 @@ int sip_find_outbound_proxy(sip_ticket_t *ticket, struct in_addr *addr,
|
||||
int sip_find_direction(sip_ticket_t *ticket, int *urlidx); /*X*/
|
||||
int sip_fixup_asterisk(char *buff, size_t *buflen); /*X*/
|
||||
int sip_obscure_callid(sip_ticket_t *ticket); /*X*/
|
||||
int sip_add_received_param(sip_ticket_t *ticket); /*X*/
|
||||
int sip_get_received_param(sip_ticket_t *ticket,
|
||||
struct in_addr *dest, int *port); /*X*/
|
||||
|
||||
/* readconf.c */
|
||||
int read_config(char *name, int search, cfgopts_t cfgopts[], char *filter); /*X*/
|
||||
int make_default_config(void); /*X*/
|
||||
|
||||
/* rtpproxy.c */
|
||||
int rtpproxy_init( void ); /*X*/
|
||||
@@ -262,12 +275,14 @@ int unload_plugins(void);
|
||||
#define DEFAULT_MAXFWD 70 /* default Max-Forward count */
|
||||
#define DEFAULT_EXPIRES 3600 /* default Expires timeout */
|
||||
|
||||
#define TCP_IDLE_TO 300 /* TCP connection idle timeout in seconds */
|
||||
#define TCP_CONNECT_TO 500 /* TCP connect() timeout in msec */
|
||||
|
||||
#define URLMAP_SIZE 128 /* number of URL mapping table entries */
|
||||
/* this limits the number of clients! */
|
||||
|
||||
#define SOURCECACHE_SIZE 256 /* number of return addresses */
|
||||
#define DEJITTERLIMIT 1500000 /* max value for dejitter configuration */
|
||||
#define DEFAULT_DEJITTER 100000 /* default value for dejitter configuration */
|
||||
|
||||
#define RTPPROXY_SIZE 256 /* number of rtp proxy entries */
|
||||
/* this limits the number of calls! */
|
||||
@@ -302,7 +317,7 @@ int unload_plugins(void);
|
||||
|
||||
/* constants for security testing */
|
||||
#define SEC_MINLEN 16 /* minimum received length */
|
||||
#define SEC_MAXLINELEN 1024 /* maximum acceptable length of one line
|
||||
#define SEC_MAXLINELEN 2048 /* maximum acceptable length of one line
|
||||
in the SIP telegram (security check)
|
||||
Careful: Proxy-Authorization lines may
|
||||
get quite long */
|
||||
|
||||
681
src/sock.c
681
src/sock.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2002-2008 Thomas Ries <tries@gmx.net>
|
||||
Copyright (C) 2002-2009 Thomas Ries <tries@gmx.net>
|
||||
|
||||
This file is part of Siproxd.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@@ -44,12 +45,35 @@ static char const ident[]="$Id$";
|
||||
/* configuration storage */
|
||||
extern struct siproxd_config configuration;
|
||||
|
||||
/* socket used for sending SIP datagrams */
|
||||
|
||||
/* static functions */
|
||||
static void tcp_expire(void);
|
||||
static int tcp_add(struct sockaddr_in addr, int fd);
|
||||
static int tcp_connect(struct sockaddr_in dst_addr);
|
||||
static int tcp_remove(int idx);
|
||||
|
||||
/* module local variables */
|
||||
|
||||
/* UDP socket used for SIP datagrams */
|
||||
int sip_udp_socket=0;
|
||||
|
||||
/* TCP listen socket used for SIP */
|
||||
int sip_tcp_socket=0;
|
||||
|
||||
/* TCP sockets used for SIP connections (twice the max number of clients) */
|
||||
struct {
|
||||
int fd; /* file descriptor, 0=unused */
|
||||
struct sockaddr_in dst_addr; /* remote target of TCP connection */
|
||||
time_t traffic_ts; /* last 'alive' TS (real SIP traffic) */
|
||||
time_t keepalive_ts; /* last 'alive' TS */
|
||||
int rxbuf_size;
|
||||
int rxbuf_len;
|
||||
char *rx_buffer;
|
||||
} sip_tcp_cache[2*URLMAP_SIZE];
|
||||
|
||||
|
||||
/*
|
||||
* binds to SIP UDP socket for listening to incoming packets
|
||||
* binds to SIP UDP and TCP sockets for listening to incoming packets
|
||||
*
|
||||
* RETURNS
|
||||
* STS_SUCCESS on success
|
||||
@@ -58,9 +82,11 @@ int sip_udp_socket=0;
|
||||
int sipsock_listen (void) {
|
||||
struct in_addr ipaddr;
|
||||
|
||||
/* listen on UDP port */
|
||||
memset(&ipaddr, 0, sizeof(ipaddr));
|
||||
sip_udp_socket=sockbind(ipaddr, configuration.sip_listen_port, 1);
|
||||
if (sip_udp_socket == 0) return STS_FAILURE; /* failure*/
|
||||
sip_udp_socket=sockbind(ipaddr, configuration.sip_listen_port,
|
||||
PROTO_UDP, 1);
|
||||
if (sip_udp_socket == 0) return STS_FAILURE; /* failure */
|
||||
|
||||
/* set DSCP value, need to be ROOT */
|
||||
if (configuration.sip_dscp) {
|
||||
@@ -92,75 +118,289 @@ int sipsock_listen (void) {
|
||||
if (uid != euid) seteuid(euid);
|
||||
}
|
||||
|
||||
/* listen on TCP port */
|
||||
memset(&ipaddr, 0, sizeof(ipaddr));
|
||||
sip_tcp_socket=sockbind(ipaddr, configuration.sip_listen_port,
|
||||
PROTO_TCP, 1);
|
||||
if (sip_tcp_socket == 0) return STS_FAILURE; /* failure */
|
||||
if (listen(sip_tcp_socket, 10)) {
|
||||
ERROR("TCP listen() failed: %s", strerror(errno));
|
||||
return STS_FAILURE;
|
||||
}
|
||||
|
||||
INFO("bound to port %i", configuration.sip_listen_port);
|
||||
DEBUGC(DBCLASS_NET,"bound socket %i",sip_udp_socket);
|
||||
DEBUGC(DBCLASS_NET,"bound UDP socket=%i, TCP socket=%i",
|
||||
sip_udp_socket, sip_tcp_socket);
|
||||
|
||||
/* initialize the TCP connection cache array */
|
||||
memset(&sip_tcp_cache, 0, sizeof(sip_tcp_cache));
|
||||
|
||||
return STS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wait for incoming SIP message. After a 2 sec timeout
|
||||
* this function returns with sts=0
|
||||
* read a message from SIP listen socket (UDP datagram)
|
||||
*
|
||||
* RETURNS >0 if data received, =0 if nothing received (T/O), -1 on error
|
||||
* RETURNS number of bytes read (=0 if nothing read, <0 timeout)
|
||||
* from is modified to return the sockaddr_in of the sender
|
||||
*/
|
||||
int sipsock_wait(void) {
|
||||
int sts;
|
||||
int sipsock_waitfordata(char *buf, size_t bufsize,
|
||||
struct sockaddr_in *from, int *protocol) {
|
||||
int i, fd;
|
||||
fd_set fdset;
|
||||
struct timeval timeout;
|
||||
int highest_fd, num_fd_active;
|
||||
static struct timeval timeout={0,0};
|
||||
int length;
|
||||
socklen_t fromlen;
|
||||
|
||||
timeout.tv_sec=2;
|
||||
timeout.tv_usec=0;
|
||||
DEBUGC(DBCLASS_BABBLE,"entered sipsock_waitfordata");
|
||||
|
||||
/* we keep the select() timeout running acrosse multiple calls to
|
||||
* select(). This avoids missing select() timeouts if the system
|
||||
* is busy with a lot of SIP traffic, causing NOT doing some
|
||||
* cyclic tasks. Like this we ensure that every 'N' (N=5) seconds
|
||||
* sipsock_waitfordata will return a timeout condition.
|
||||
* Note: there is still the remote possibility that SIP packet
|
||||
* arrive so fast that select() always return data available -
|
||||
* but in this case YOU have some seroious other issues...
|
||||
*/
|
||||
if ((timeout.tv_sec== 0) && (timeout.tv_usec == 0)) {
|
||||
DEBUGC(DBCLASS_BABBLE,"winding up select() timeout");
|
||||
timeout.tv_sec=5;
|
||||
timeout.tv_usec=0;
|
||||
}
|
||||
|
||||
/* prepare FD set: UDP, TCP listen */
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET (sip_udp_socket, &fdset);
|
||||
sts=select (sip_udp_socket+1, &fdset, NULL, NULL, &timeout);
|
||||
FD_SET (sip_tcp_socket, &fdset);
|
||||
if (sip_udp_socket > sip_tcp_socket) {
|
||||
highest_fd = sip_udp_socket;
|
||||
} else {
|
||||
highest_fd = sip_tcp_socket;
|
||||
}
|
||||
|
||||
/* prepare FD set: TCP connections */
|
||||
for (i=0; i<(sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0])); i++) {
|
||||
/* active TCP conenction? */
|
||||
if (sip_tcp_cache[i].fd) {
|
||||
/* add to FD set */
|
||||
FD_SET(sip_tcp_cache[i].fd, &fdset);
|
||||
if (sip_tcp_cache[i].fd > highest_fd) {
|
||||
highest_fd = sip_tcp_cache[i].fd;
|
||||
}
|
||||
} /* if fd > 0 */
|
||||
}
|
||||
|
||||
/* select() on all FD's with timeout */
|
||||
num_fd_active=select (highest_fd+1, &fdset, NULL, NULL, &timeout);
|
||||
|
||||
/* WARN on failures */
|
||||
if (sts<0) {
|
||||
if (num_fd_active < 0) {
|
||||
/* WARN on failure, except if it is an "interrupted system call"
|
||||
as it will result by SIGINT, SIGTERM */
|
||||
if (errno != 4) {
|
||||
if (errno != EINTR) {
|
||||
WARN("select() returned error [%i:%s]",errno, strerror(errno));
|
||||
} else {
|
||||
DEBUGC(DBCLASS_NET,"select() returned error [%i:%s]",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
/*
|
||||
* read a message from SIP listen socket (UDP datagram)
|
||||
*
|
||||
* RETURNS number of bytes read
|
||||
* from is modified to return the sockaddr_in of the sender
|
||||
*/
|
||||
int sipsock_read(void *buf, size_t bufsize,
|
||||
struct sockaddr_in *from, int *protocol) {
|
||||
int count;
|
||||
socklen_t fromlen;
|
||||
|
||||
fromlen=sizeof(struct sockaddr_in);
|
||||
*protocol = PROTO_UDP; /* up to now, only UDP */
|
||||
count=recvfrom(sip_udp_socket, buf, bufsize, 0,
|
||||
(struct sockaddr *)from, &fromlen);
|
||||
|
||||
if (count<0) {
|
||||
WARN("recvfrom() returned error [%s]",strerror(errno));
|
||||
*protocol = PROTO_UNKN;
|
||||
/* nothing here = timeout condition */
|
||||
if (num_fd_active <= 0) {
|
||||
/* process the active TCP connection list - expire old entries */
|
||||
tcp_expire();
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_NET,"received UDP packet from %s, count=%i",
|
||||
utils_inet_ntoa(from->sin_addr), count);
|
||||
DUMP_BUFFER(DBCLASS_NETTRAF, buf, count);
|
||||
|
||||
return count;
|
||||
for (i=0; i< highest_fd; i++) {
|
||||
if (FD_ISSET(i, &fdset)) DEBUGC(DBCLASS_BABBLE, "FD %i = active", i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some FD's have signalled that data is available (fdset)
|
||||
* Process them:
|
||||
* - UDP socket: read data and return
|
||||
* - TCP listen socket: Accept connection, update TCP cache and return
|
||||
* - TCP connection socket: read data, update alive timestamp & return
|
||||
* In case of disconnected socket (recv error [all but EAGAIN, EINTR])
|
||||
* close connection
|
||||
*/
|
||||
|
||||
/* Strategy to get get data from the FD's:
|
||||
* 1) check TCP listen socket, if connection pending ACCEPT
|
||||
* 2) check UDP socket. If data available, process that & return
|
||||
* 3) check TCP sockets, take first in table with data & return
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Check TCP listen socket
|
||||
*/
|
||||
if (FD_ISSET(sip_tcp_socket, &fdset)) {
|
||||
fromlen=sizeof(struct sockaddr_in);
|
||||
fd = accept(sip_tcp_socket, (struct sockaddr *)from, &fromlen);
|
||||
if (fd < 0) {
|
||||
WARN("accept() returned error [%i:%s]",errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
i=tcp_add(*from, fd);
|
||||
if (i < 0) {
|
||||
ERROR("out of space in TCP connection cache - rejecting");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_NET, "accepted TCP connection from [%s] fd=%i",
|
||||
utils_inet_ntoa(from->sin_addr), fd);
|
||||
|
||||
num_fd_active--;
|
||||
if (num_fd_active <=0) return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check UDP socket
|
||||
*/
|
||||
if (FD_ISSET(sip_udp_socket, &fdset)) {
|
||||
*protocol = PROTO_UDP;
|
||||
|
||||
fromlen=sizeof(struct sockaddr_in);
|
||||
length=recvfrom(sip_udp_socket, buf, bufsize, 0,
|
||||
(struct sockaddr *)from, &fromlen);
|
||||
|
||||
if (length < 0) {
|
||||
WARN("recvfrom() returned error [%s]",strerror(errno));
|
||||
length=0;
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_NET,"received UDP packet from [%s:%i] count=%i",
|
||||
utils_inet_ntoa(from->sin_addr), ntohs(from->sin_port), length);
|
||||
DUMP_BUFFER(DBCLASS_NETTRAF, buf, length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check active TCP sockets
|
||||
*/
|
||||
for (i=0; i<(sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0])); i++) {
|
||||
if (sip_tcp_cache[i].fd == 0) continue;
|
||||
|
||||
/* no more active FD's to be expected, exit the loop */
|
||||
if (num_fd_active <= 0) break;
|
||||
|
||||
if (FD_ISSET(sip_tcp_cache[i].fd, &fdset)) {
|
||||
/* found a match */
|
||||
DEBUGC(DBCLASS_BABBLE,"matched active TCP fd=%i idx=%i",
|
||||
sip_tcp_cache[i].fd, i);
|
||||
|
||||
num_fd_active--;
|
||||
*protocol = PROTO_TCP;
|
||||
memcpy(from, &sip_tcp_cache[i].dst_addr, sizeof(struct sockaddr_in));
|
||||
|
||||
length = recv(sip_tcp_cache[i].fd, buf, bufsize, 0);
|
||||
if (length < 0) {
|
||||
WARN("recv() returned error [%s], disconnecting TCP [%s] fd=%i",
|
||||
strerror(errno), utils_inet_ntoa(from->sin_addr),
|
||||
sip_tcp_cache[i].fd);
|
||||
length=0;
|
||||
tcp_remove(i);
|
||||
}
|
||||
if (length == 0) {
|
||||
/* length=0 indicates a disconnect from remote side */
|
||||
DEBUGC(DBCLASS_NET, "received TCP disconnect [%s:%i] fd=%i",
|
||||
utils_inet_ntoa(from->sin_addr), ntohs(from->sin_port),
|
||||
sip_tcp_cache[i].fd);
|
||||
tcp_remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* prematurely check for <CR><LF> keepalives, no need to do any
|
||||
work on them... Set length = 0 and done. */
|
||||
if (length == 2 && (memcmp(buf, "\x0d\x0a", 2) == 0)) {
|
||||
DEBUGC(DBCLASS_NET, "got a SIP TCP keepalive from [%s:%i] fd=%i",
|
||||
utils_inet_ntoa(from->sin_addr), ntohs(from->sin_port),
|
||||
sip_tcp_cache[i].fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_NET,"received TCP packet from [%s:%i] count=%i fd=%i",
|
||||
utils_inet_ntoa(from->sin_addr), ntohs(from->sin_port),
|
||||
length, sip_tcp_cache[i].fd);
|
||||
DUMP_BUFFER(DBCLASS_NETTRAF, buf, length);
|
||||
|
||||
/* check for <CR><LF> termination of TCP RX buffer */
|
||||
if ((length > 2) &&
|
||||
(memcmp(&buf[length-2], "\x0d\x0a", 2) != 0)) {
|
||||
/* not terminated */
|
||||
DEBUGC(DBCLASS_NET, "received incomplete fragment, buffering...");
|
||||
/* append to RX buffer of his connection */
|
||||
if (sip_tcp_cache[i].rxbuf_len+length < sip_tcp_cache[i].rxbuf_size) {
|
||||
memcpy(&sip_tcp_cache[i].rx_buffer[sip_tcp_cache[i].rxbuf_len],
|
||||
buf, length);
|
||||
sip_tcp_cache[i].rxbuf_len+=length;
|
||||
} else {
|
||||
/* out of RX buffer space, discard this SIP frame */
|
||||
DEBUGC(DBCLASS_NET, "RX buffer too small, discarding this frame");
|
||||
sip_tcp_cache[i].rxbuf_len=0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
/* terminated by <CR><LF> */
|
||||
if (sip_tcp_cache[i].rxbuf_len != 0) {
|
||||
/* have already buffered data waiting. Copy new fragment to end
|
||||
* of RX buffer and then copy all back... */
|
||||
|
||||
if (sip_tcp_cache[i].rxbuf_len+length < sip_tcp_cache[i].rxbuf_size) {
|
||||
DEBUGC(DBCLASS_NET, "received last fragment, assembling...");
|
||||
memcpy(&sip_tcp_cache[i].rx_buffer[sip_tcp_cache[i].rxbuf_len],
|
||||
buf, length);
|
||||
sip_tcp_cache[i].rxbuf_len+=length;
|
||||
} else {
|
||||
/* out of RX buffer space, discard this SIP frame */
|
||||
DEBUGC(DBCLASS_NET, "RX buffer too small, discarding this frame");
|
||||
sip_tcp_cache[i].rxbuf_len=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy whole RX buffer to the callers buffer */
|
||||
if (sip_tcp_cache[i].rxbuf_len <= bufsize) {
|
||||
memcpy (buf, sip_tcp_cache[i].rx_buffer, sip_tcp_cache[i].rxbuf_len);
|
||||
length = sip_tcp_cache[i].rxbuf_len;
|
||||
} else {
|
||||
/* TCP RX buffer bigger than callers buffer... */
|
||||
DEBUGC(DBCLASS_NET, "buffer passed to sipsock_waitfordata is too small");
|
||||
sip_tcp_cache[i].rxbuf_len=0;
|
||||
length =0;
|
||||
}
|
||||
}
|
||||
|
||||
/* update activity timestamp */
|
||||
if (length > 0) {
|
||||
time(&sip_tcp_cache[i].traffic_ts);
|
||||
sip_tcp_cache[i].keepalive_ts=sip_tcp_cache[i].traffic_ts;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
} /* FD_ISSET(sip_tcp_cache[i].fd, &fdset */
|
||||
} /* for i */
|
||||
|
||||
/* no data found to be processed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* sends an UDP datagram to the specified destination
|
||||
* sends an SIP datagram (UDP or TCP) to the specified destination
|
||||
*
|
||||
* RETURNS
|
||||
* STS_SUCCESS on success
|
||||
@@ -170,6 +410,7 @@ int sipsock_send(struct in_addr addr, int port, int protocol,
|
||||
char *buffer, size_t size) {
|
||||
struct sockaddr_in dst_addr;
|
||||
int sts;
|
||||
int i;
|
||||
|
||||
/* first time: allocate a socket for sending */
|
||||
if (sip_udp_socket == 0) {
|
||||
@@ -182,31 +423,77 @@ int sipsock_send(struct in_addr addr, int port, int protocol,
|
||||
return STS_FAILURE;
|
||||
}
|
||||
|
||||
if (protocol != PROTO_UDP) {
|
||||
ERROR("sipsock_send: only UDP supported by now");
|
||||
return STS_FAILURE;
|
||||
}
|
||||
if (protocol == PROTO_UDP) {
|
||||
/*
|
||||
* UDP target
|
||||
*/
|
||||
dst_addr.sin_family = AF_INET;
|
||||
memcpy(&dst_addr.sin_addr, &addr, sizeof(struct in_addr));
|
||||
dst_addr.sin_port= htons(port);
|
||||
|
||||
dst_addr.sin_family = AF_INET;
|
||||
memcpy(&dst_addr.sin_addr.s_addr, &addr, sizeof(struct in_addr));
|
||||
dst_addr.sin_port= htons(port);
|
||||
DEBUGC(DBCLASS_NET,"send UDP packet to %s: %i", utils_inet_ntoa(addr),port);
|
||||
DUMP_BUFFER(DBCLASS_NETTRAF, buffer, size);
|
||||
|
||||
DEBUGC(DBCLASS_NET,"send UDP packet to %s: %i", utils_inet_ntoa(addr),port);
|
||||
DUMP_BUFFER(DBCLASS_NETTRAF, buffer, size);
|
||||
sts = sendto(sip_udp_socket, buffer, size, 0,
|
||||
(const struct sockaddr *)&dst_addr,
|
||||
(socklen_t)sizeof(dst_addr));
|
||||
|
||||
sts = sendto(sip_udp_socket, buffer, size, 0,
|
||||
(const struct sockaddr *)&dst_addr,
|
||||
(socklen_t)sizeof(dst_addr));
|
||||
|
||||
if (sts == -1) {
|
||||
if (errno != ECONNREFUSED) {
|
||||
ERROR("sendto() [%s:%i size=%ld] call failed: %s",
|
||||
if (sts == -1) {
|
||||
if (errno != ECONNREFUSED) {
|
||||
ERROR("sendto() [%s:%i size=%ld] call failed: %s",
|
||||
utils_inet_ntoa(addr),
|
||||
port, (long)size, strerror(errno));
|
||||
return STS_FAILURE;
|
||||
}
|
||||
DEBUGC(DBCLASS_BABBLE,"sendto() [%s:%i] call failed: %s",
|
||||
utils_inet_ntoa(addr), port, strerror(errno));
|
||||
}
|
||||
} else if (protocol == PROTO_TCP) {
|
||||
/*
|
||||
* TCP target
|
||||
*/
|
||||
dst_addr.sin_family = AF_INET;
|
||||
memcpy(&dst_addr.sin_addr, &addr, sizeof(struct in_addr));
|
||||
dst_addr.sin_port= htons(port);
|
||||
|
||||
/* check connection cache for an existing TCP connection */
|
||||
i=tcp_find(dst_addr);
|
||||
|
||||
/* if no TCP connection found, do a connect (non blocking) and add to list */
|
||||
if (i < 0) {
|
||||
DEBUGC(DBCLASS_NET,"no TCP connection found to %s:%i - connecting",
|
||||
utils_inet_ntoa(addr), port);
|
||||
|
||||
i=tcp_connect(dst_addr);
|
||||
if (i < 0) {
|
||||
ERROR("tcp_connect() failed");
|
||||
return STS_FAILURE;
|
||||
}
|
||||
|
||||
} /* if i */
|
||||
|
||||
/* send data and update alive timestamp */
|
||||
DEBUGC(DBCLASS_NET,"send TCP packet to %s:%i", utils_inet_ntoa(addr), port);
|
||||
DUMP_BUFFER(DBCLASS_NETTRAF, buffer, size);
|
||||
|
||||
time(&sip_tcp_cache[i].traffic_ts);
|
||||
sip_tcp_cache[i].keepalive_ts=sip_tcp_cache[i].traffic_ts;
|
||||
|
||||
sts = send(sip_tcp_cache[i].fd, buffer, size, 0);
|
||||
|
||||
if (sts == -1) {
|
||||
ERROR("send() [%s:%i size=%ld] call failed: %s",
|
||||
utils_inet_ntoa(addr),
|
||||
port, (long)size, strerror(errno));
|
||||
return STS_FAILURE;
|
||||
}
|
||||
DEBUGC(DBCLASS_BABBLE,"sendto() [%s:%i] call failed: %s",
|
||||
utils_inet_ntoa(addr), port, strerror(errno));
|
||||
|
||||
} else {
|
||||
/*
|
||||
* unknown/unsupported protocol
|
||||
*/
|
||||
ERROR("sipsock_send: only UDP and TCP supported by now");
|
||||
return STS_FAILURE;
|
||||
}
|
||||
|
||||
return STS_SUCCESS;
|
||||
@@ -221,7 +508,7 @@ int sipsock_send(struct in_addr addr, int port, int protocol,
|
||||
*
|
||||
* RETURNS socket number on success, zero on failure
|
||||
*/
|
||||
int sockbind(struct in_addr ipaddr, int localport, int errflg) {
|
||||
int sockbind(struct in_addr ipaddr, int localport, int protocol, int errflg) {
|
||||
struct sockaddr_in my_addr;
|
||||
int sts, on=1;
|
||||
int sock;
|
||||
@@ -230,10 +517,17 @@ int sockbind(struct in_addr ipaddr, int localport, int errflg) {
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
|
||||
my_addr.sin_family = AF_INET;
|
||||
memcpy(&my_addr.sin_addr.s_addr, &ipaddr, sizeof(struct in_addr));
|
||||
memcpy(&my_addr.sin_addr, &ipaddr, sizeof(struct in_addr));
|
||||
my_addr.sin_port = htons(localport);
|
||||
|
||||
sock=socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (protocol == PROTO_UDP) {
|
||||
sock=socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
} else if (protocol == PROTO_TCP) {
|
||||
sock=socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
} else {
|
||||
if (errflg) ERROR("invalig protocol: %i", protocol);
|
||||
return 0;
|
||||
}
|
||||
if (sock < 0) {
|
||||
ERROR("socket call failed: %s",strerror(errno));
|
||||
return 0;
|
||||
@@ -277,3 +571,260 @@ int sockbind(struct in_addr ipaddr, int localport, int errflg) {
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* age and expire TCP connections
|
||||
*
|
||||
* RETURNS: -
|
||||
*/
|
||||
static void tcp_expire(void) {
|
||||
time_t now;
|
||||
time_t to_limit;
|
||||
int i;
|
||||
int sts;
|
||||
|
||||
time(&now);
|
||||
to_limit = now - configuration.tcp_timeout;
|
||||
|
||||
for (i=0; i<(sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0])); i++) {
|
||||
if (sip_tcp_cache[i].fd == 0) continue;
|
||||
|
||||
if (sip_tcp_cache[i].traffic_ts < to_limit) {
|
||||
/* TCP has expired, close & cleanup */
|
||||
DEBUGC(DBCLASS_NET, "TCP inactivity T/O, disconnecting: [%s] fd=%i",
|
||||
utils_inet_ntoa((&sip_tcp_cache[i].dst_addr)->sin_addr),
|
||||
sip_tcp_cache[i].fd);
|
||||
tcp_remove(i);
|
||||
} else
|
||||
|
||||
/* TCP keepalive handling */
|
||||
if ((sip_tcp_cache[i].keepalive_ts + configuration.tcp_keepalive) <= now) {
|
||||
DEBUGC(DBCLASS_NET, "sending TCP keepalive [%s:%i] fd=%i idx=%i",
|
||||
utils_inet_ntoa(sip_tcp_cache[i].dst_addr.sin_addr),
|
||||
ntohs(sip_tcp_cache[i].dst_addr.sin_port), sip_tcp_cache[i].fd, i);
|
||||
|
||||
sip_tcp_cache[i].keepalive_ts = now;
|
||||
|
||||
sts = send(sip_tcp_cache[i].fd, "\x0d\x0a", 2, 0);
|
||||
|
||||
if (sts == -1) {
|
||||
WARN("keepalive send() failed: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
} /* for */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* find a TCP connection in cache
|
||||
*
|
||||
* RETURNS: index into TCP cache or -1 on not found
|
||||
*/
|
||||
int tcp_find(struct sockaddr_in dst_addr) {
|
||||
int i;
|
||||
|
||||
/* check connection cache for an existing TCP connection */
|
||||
for (i=0; i<(sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0])); i++) {
|
||||
/* occupied entry? */
|
||||
if (sip_tcp_cache[i].fd == 0) continue;
|
||||
|
||||
/* address & port match */
|
||||
if ((memcmp(&dst_addr.sin_addr, &sip_tcp_cache[i].dst_addr.sin_addr,
|
||||
sizeof(struct in_addr)) ==0) &&
|
||||
(dst_addr.sin_port==sip_tcp_cache[i].dst_addr.sin_port)) break;
|
||||
} /* for */
|
||||
|
||||
/* if no TCP connection found return -1 */
|
||||
if (i >= (sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0]))) return -1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* add a TCP connection into cache
|
||||
*
|
||||
* RETURNS: index into TCP cache or -1 on failure (out of space)
|
||||
*/
|
||||
static int tcp_add(struct sockaddr_in addr, int fd) {
|
||||
int i;
|
||||
|
||||
/* find free entry in TCP cache */
|
||||
for (i=0; i<(sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0])); i++) {
|
||||
if (sip_tcp_cache[i].fd == 0) break;
|
||||
}
|
||||
if (i >= (sizeof(sip_tcp_cache)/sizeof(sip_tcp_cache[0]))) {
|
||||
DEBUGC(DBCLASS_NET, "out of space in TCP cache [%s] fd=%i",
|
||||
utils_inet_ntoa(addr.sin_addr), fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* store connection data in TCP cache */
|
||||
sip_tcp_cache[i].fd = fd;
|
||||
memcpy(&sip_tcp_cache[i].dst_addr, &addr, sizeof(struct sockaddr_in));
|
||||
time(&sip_tcp_cache[i].traffic_ts);
|
||||
sip_tcp_cache[i].keepalive_ts=sip_tcp_cache[i].traffic_ts;
|
||||
|
||||
/* sanity check: must be unallocated */
|
||||
if (sip_tcp_cache[i].rx_buffer != NULL) {
|
||||
WARN("sip_tcp_cache[%i].rx_buffer was not freed! Potential memleak.", i);
|
||||
free(sip_tcp_cache[i].rx_buffer);
|
||||
sip_tcp_cache[i].rx_buffer = NULL;
|
||||
}
|
||||
|
||||
/* allocate RX buffer */
|
||||
sip_tcp_cache[i].rx_buffer=malloc(BUFFER_SIZE);
|
||||
if (sip_tcp_cache[i].rx_buffer == NULL) {
|
||||
DEBUGC(DBCLASS_NET, "malloc() of %i bytes failed", BUFFER_SIZE);
|
||||
return -1;
|
||||
}
|
||||
sip_tcp_cache[i].rxbuf_size=BUFFER_SIZE;
|
||||
sip_tcp_cache[i].rxbuf_len=0;
|
||||
|
||||
|
||||
DEBUGC(DBCLASS_NET, "added TCP connection [%s] fd=%i to cache idx=%i",
|
||||
utils_inet_ntoa(addr.sin_addr), fd, i);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* connect to a remote TCP target
|
||||
*
|
||||
* RETURNS: index into TCP cache or -1 on failure
|
||||
*/
|
||||
static int tcp_connect(struct sockaddr_in dst_addr) {
|
||||
int sock;
|
||||
int flags;
|
||||
int sts;
|
||||
int i;
|
||||
struct timeval timeout={0,0};
|
||||
fd_set fdset;
|
||||
|
||||
/* get socket and connect to remote site */
|
||||
sock=socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock < 0) {
|
||||
ERROR("socket() call failed: %s",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* non blocking */
|
||||
flags = fcntl(sock, F_GETFL);
|
||||
if (flags < 0) {
|
||||
ERROR("fcntl(F_SETFL) failed: %s",strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
if (fcntl(sock, F_SETFL, (long) flags | O_NONBLOCK) < 0) {
|
||||
ERROR("fcntl(F_SETFL) failed: %s",strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sts=connect(sock, &dst_addr, sizeof(struct sockaddr_in));
|
||||
if ((sts == -1 ) && (errno == EINPROGRESS)) {
|
||||
/* if non-blocking connect(), wait until connection
|
||||
successful, discarded or timeout */
|
||||
DEBUGC(DBCLASS_NET, "connection in progress, waiting %i msec to succeed",
|
||||
configuration.tcp_connect_timeout);
|
||||
|
||||
/* timeout for connect */
|
||||
timeout.tv_sec = (configuration.tcp_connect_timeout/1000);
|
||||
timeout.tv_usec = (configuration.tcp_connect_timeout%1000)*1000;
|
||||
|
||||
do {
|
||||
/* prepare fd set */
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(sock, &fdset);
|
||||
sts = select(sock+1, NULL, &fdset, NULL, &timeout);
|
||||
if ((sts < 0) && (errno == EINTR)) {
|
||||
/* select() has been interrupted, do it again */
|
||||
continue;
|
||||
} else if (sts < 0) {
|
||||
ERROR("waiting for TCP connect failed: %s",strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
} else if (sts > 0) {
|
||||
/* fd available for write */
|
||||
int valopt;
|
||||
int optlen=sizeof(valopt);
|
||||
|
||||
/* get error status from delayed connect() */
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
|
||||
(void*)(&valopt), &optlen) < 0) {
|
||||
ERROR("getsockopt(SO_ERROR) failed: %s",strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (valopt == EINPROGRESS) {
|
||||
ERROR("connect() returned: %s",strerror(valopt));
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_NET, "connect() completed after %i msec",
|
||||
(int)(configuration.tcp_connect_timeout
|
||||
- (timeout.tv_sec*1000)
|
||||
- (timeout.tv_usec/1000)));
|
||||
|
||||
/* check the returned error value from connect() */
|
||||
if (valopt) {
|
||||
ERROR("delayed TCP connect() failed : %s",strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
/* all went fine, continue */
|
||||
break;
|
||||
|
||||
} else {
|
||||
DEBUGC(DBCLASS_NET, "tcp_connect() timeout");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
} else if (sts == -1 ) {
|
||||
if ((errno != ECONNREFUSED) && (errno != ETIMEDOUT)) {
|
||||
ERROR("connect() [%s:%i] call failed: %s",
|
||||
utils_inet_ntoa(dst_addr.sin_addr),
|
||||
ntohs(dst_addr.sin_port), strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
DEBUGC(DBCLASS_BABBLE,"connect() [%s:%i] call failed: %s",
|
||||
utils_inet_ntoa(dst_addr.sin_addr),
|
||||
ntohs(dst_addr.sin_port), strerror(errno));
|
||||
}
|
||||
|
||||
i=tcp_add(dst_addr, sock);
|
||||
if (i < 0) {
|
||||
ERROR("out of space in TCP connection cache - rejecting");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUGC(DBCLASS_NET, "connected TCP connection to [%s:%i] fd=%i",
|
||||
utils_inet_ntoa(dst_addr.sin_addr),
|
||||
ntohs(dst_addr.sin_port), sock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* clean up resources occupied by a TCP entry
|
||||
*
|
||||
* RETURNS: 0
|
||||
*/
|
||||
static int tcp_remove(int idx) {
|
||||
close(sip_tcp_cache[idx].fd);
|
||||
sip_tcp_cache[idx].fd=0;
|
||||
free(sip_tcp_cache[idx].rx_buffer);
|
||||
sip_tcp_cache[idx].rx_buffer=NULL;
|
||||
sip_tcp_cache[idx].rxbuf_size=0;
|
||||
sip_tcp_cache[idx].rxbuf_len=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user