📄 nathelper.c
字号:
/* $Id: nathelper.c,v 1.67.2.5 2005/07/20 17:11:51 andrei Exp $ * * Copyright (C) 2003 Porta Software Ltd * * 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 * * History: * --------- * 2003-10-09 nat_uac_test introduced (jiri) * * 2003-11-06 nat_uac_test permitted from onreply_route (jiri) * * 2003-12-01 unforce_rtp_proxy introduced (sobomax) * * 2004-01-07 RTP proxy support updated to support new version of the * RTP proxy (20040107). * * force_rtp_proxy() now inserts a special flag * into the SDP body to indicate that this session already * proxied and ignores sessions with such flag. * * Added run-time check for version of command protocol * supported by the RTP proxy. * * 2004-01-16 Integrated slightly modified patch from Tristan Colgate, * force_rtp_proxy function with IP as a parameter (janakj) * * 2004-01-28 nat_uac_test extended to allow testing SDP body (sobomax) * * nat_uac_test extended to allow testing top Via (sobomax) * * 2004-02-21 force_rtp_proxy now accepts option argument, which * consists of string of chars, each of them turns "on" * some feature, currently supported ones are: * * `a' - flags that UA from which message is received * doesn't support symmetric RTP; * `l' - force "lookup", that is, only rewrite SDP when * corresponding session is already exists in the * RTP proxy. Only makes sense for SIP requests, * replies are always processed in "lookup" mode; * `i' - flags that message is received from UA in the * LAN. Only makes sense when RTP proxy is running * in the bridge mode. * * force_rtp_proxy can now be invoked without any arguments, * as previously, with one argument - in this case argument * is treated as option string and with two arguments, in * which case 1st argument is option string and the 2nd * one is IP address which have to be inserted into * SDP (IP address on which RTP proxy listens). * * 2004-03-12 Added support for IPv6 addresses in SDPs. Particularly, * force_rtp_proxy now can work with IPv6-aware RTP proxy, * replacing IPv4 address in SDP with IPv6 one and vice versa. * This allows creating full-fledged IPv4<->IPv6 gateway. * See 4to6.cfg file for example. * * Two new options added into force_rtp_proxy: * * `f' - instructs nathelper to ignore marks inserted * by another nathelper in transit to indicate * that the session is already goes through another * proxy. Allows creating chain of proxies. * `r' - flags that IP address in SDP should be trusted. * Without this flag, nathelper ignores address in the * SDP and uses source address of the SIP message * as media address which is passed to the RTP proxy. * * Protocol between nathelper and RTP proxy in bridge * mode has been slightly changed. Now RTP proxy expects SER * to provide 2 flags when creating or updating session * to indicate direction of this session. Each of those * flags can be either `e' or `i'. For example `ei' means * that we received INVITE from UA on the "external" network * network and will send it to the UA on "internal" one. * Also possible `ie' (internal->external), `ii' * (internal->internal) and `ee' (external->external). See * example file alg.cfg for details. * * 2004-03-15 If the rtp proxy test failed (wrong version or not started) * retry test from time to time, when some *rtpproxy* function * is invoked. Minimum interval between retries can be * configured via rtpproxy_disable_tout module parameter (default * is 60 seconds). Setting it to -1 will disable periodic * rechecks completely, setting it to 0 will force checks * for each *rtpproxy* function call. (andrei) * * 2004-03-22 Fix assignment of rtpproxy_retr and rtpproxy_tout module * parameters. * * 2004-03-22 Fix get_body position (should be called before get_callid) * (andrei) * 2004-03-24 Fix newport for null ip address case (e.g onhold re-INVITE) * (andrei) * 2004-09-30 added received port != via port test (andrei) * 2004-10-10 force_socket option introduced (jiri) * */#include "nhelpr_funcs.h"#include "../../flags.h"#include "../../sr_module.h"#include "../../dprint.h"#include "../../data_lump.h"#include "../../data_lump_rpl.h"#include "../../error.h"#include "../../forward.h"#include "../../mem/mem.h"#include "../../parser/parse_from.h"#include "../../parser/parse_to.h"#include "../../parser/parse_uri.h"#include "../../parser/parser_f.h"#include "../../resolve.h"#include "../../timer.h"#include "../../trim.h"#include "../../ut.h"#include "../registrar/sip_msg.h"#include "../../msg_translator.h"#include "../usrloc/usrloc.h"#include "../../usr_avp.h"#include "../../socket_info.h"#include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/uio.h>#include <sys/un.h>#include <ctype.h>#include <errno.h>#include <netdb.h>#include <poll.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>MODULE_VERSION#if !defined(AF_LOCAL)#define AF_LOCAL AF_UNIX#endif#if !defined(PF_LOCAL)#define PF_LOCAL PF_UNIX#endif/* NAT UAC test constants */#define NAT_UAC_TEST_C_1918 0x01#define NAT_UAC_TEST_RCVD 0x02#define NAT_UAC_TEST_V_1918 0x04#define NAT_UAC_TEST_S_1918 0x08#define NAT_UAC_TEST_RPORT 0x10/* Handy macros */#define STR2IOVEC(sx, ix) {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;}/* Supported version of the RTP proxy command protocol */#define SUP_CPROTOVER 20040107#define CPORT "22222"static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2);static int fix_nated_contact_f(struct sip_msg *, char *, char *);static int fix_nated_sdp_f(struct sip_msg *, char *, char *);static int extract_mediaip(str *, str *, int *);static int extract_mediaport(str *, str *);static int alter_mediaip(struct sip_msg *, str *, str *, int, str *, int, int);static int alter_mediaport(struct sip_msg *, str *, str *, str *, int);static char *gencookie();static int rtpp_test(int, int);static char *send_rtpp_command(struct iovec *, int);static int unforce_rtp_proxy_f(struct sip_msg *, char *, char *);static int force_rtp_proxy0_f(struct sip_msg *, char *, char *);static int force_rtp_proxy1_f(struct sip_msg *, char *, char *);static int force_rtp_proxy2_f(struct sip_msg *, char *, char *);static int fix_nated_register_f(struct sip_msg *, char *, char *);static int add_rcv_param_f(struct sip_msg *, char *, char *);static void timer(unsigned int, void *);inline static int fixup_str2int(void**, int);static int mod_init(void);static int child_init(int);static usrloc_api_t ul;static int cblen = 0;static int natping_interval = 0;struct socket_info* force_socket = 0;static struct { const char *cnetaddr; uint32_t netaddr; uint32_t mask;} nets_1918[] = { {"10.0.0.0", 0, 0xffffffffu << 24}, {"172.16.0.0", 0, 0xffffffffu << 20}, {"192.168.0.0", 0, 0xffffffffu << 16}, {NULL, 0, 0}};/* * If this parameter is set then the natpinger will ping only contacts * that have the NAT flag set in user location database */static int ping_nated_only = 0;static const char sbuf[4] = {0, 0, 0, 0};static char *rtpproxy_sock = "unix:/var/run/rtpproxy.sock";static char *force_socket_str = 0;static int rtpproxy_disable = 0;static int rtpproxy_disable_tout = 60;static int rtpproxy_retr = 5;static int rtpproxy_tout = 1;static int umode = 0;static int controlfd;static pid_t mypid;static unsigned int myseqn = 0;static int rcv_avp_no=42;static cmd_export_t cmds[] = { {"fix_nated_contact", fix_nated_contact_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"fix_nated_sdp", fix_nated_sdp_f, 1, fixup_str2int, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"unforce_rtp_proxy", unforce_rtp_proxy_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"force_rtp_proxy", force_rtp_proxy0_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"force_rtp_proxy", force_rtp_proxy1_f, 1, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"force_rtp_proxy", force_rtp_proxy2_f, 2, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, {"nat_uac_test", nat_uac_test_f, 1, fixup_str2int, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, {"fix_nated_register", fix_nated_register_f, 0, 0, REQUEST_ROUTE }, {"add_rcv_param", add_rcv_param_f, 0, 0, REQUEST_ROUTE }, {0, 0, 0, 0, 0}};static param_export_t params[] = { {"natping_interval", INT_PARAM, &natping_interval }, {"ping_nated_only", INT_PARAM, &ping_nated_only }, {"rtpproxy_sock", STR_PARAM, &rtpproxy_sock }, {"rtpproxy_disable", INT_PARAM, &rtpproxy_disable }, {"rtpproxy_disable_tout", INT_PARAM, &rtpproxy_disable_tout }, {"rtpproxy_retr", INT_PARAM, &rtpproxy_retr }, {"rtpproxy_tout", INT_PARAM, &rtpproxy_tout }, {"received_avp", INT_PARAM, &rcv_avp_no }, {"force_socket", STR_PARAM, &force_socket_str }, {0, 0, 0}};struct module_exports exports = { "nathelper", cmds, params, mod_init, 0, /* reply processing */ 0, /* destroy function */ 0, /* on_break */ child_init};static intmod_init(void){ int i; char *cp; bind_usrloc_t bind_usrloc; struct in_addr addr; str socket_str; if (force_socket_str) { socket_str.s=force_socket_str; socket_str.len=strlen(socket_str.s); force_socket=grep_sock_info(&socket_str,0,0); } if (natping_interval > 0) { bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); if (!bind_usrloc) { LOG(L_ERR, "nathelper: Can't find usrloc module\n"); return -1; } if (bind_usrloc(&ul) < 0) { return -1; } register_timer(timer, NULL, natping_interval); } /* Prepare 1918 networks list */ for (i = 0; nets_1918[i].cnetaddr != NULL; i++) { if (inet_aton(nets_1918[i].cnetaddr, &addr) != 1) abort(); nets_1918[i].netaddr = ntohl(addr.s_addr) & nets_1918[i].mask; } if (rtpproxy_disable == 0) { /* Make rtpproxy_sock writable */ cp = pkg_malloc(strlen(rtpproxy_sock) + 1); if (cp == NULL) { LOG(L_ERR, "nathelper: Can't allocate memory\n"); return -1; } strcpy(cp, rtpproxy_sock); rtpproxy_sock = cp; if (strncmp(rtpproxy_sock, "udp:", 4) == 0) { umode = 1; rtpproxy_sock += 4; } else if (strncmp(rtpproxy_sock, "udp6:", 5) == 0) { umode = 6; rtpproxy_sock += 5; } else if (strncmp(rtpproxy_sock, "unix:", 5) == 0) { umode = 0; rtpproxy_sock += 5; } } return 0;}static intchild_init(int rank){ int n; char *cp; struct addrinfo hints, *res; if (rtpproxy_disable == 0) { mypid = getpid(); if (umode != 0) { cp = strrchr(rtpproxy_sock, ':'); if (cp != NULL) { *cp = '\0'; cp++; } if (cp == NULL || *cp == '\0') cp = CPORT; memset(&hints, 0, sizeof(hints)); hints.ai_flags = 0; hints.ai_family = (umode == 6) ? AF_INET6 : AF_INET; hints.ai_socktype = SOCK_DGRAM; if ((n = getaddrinfo(rtpproxy_sock, cp, &hints, &res)) != 0) { LOG(L_ERR, "nathelper: getaddrinfo: %s\n", gai_strerror(n)); return -1; } controlfd = socket((umode == 6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); if (controlfd == -1) { LOG(L_ERR, "nathelper: can't create socket\n"); freeaddrinfo(res); return -1; } if (connect(controlfd, res->ai_addr, res->ai_addrlen) == -1) { LOG(L_ERR, "nathelper: can't connect to a RTP proxy\n"); close(controlfd); freeaddrinfo(res); return -1; } freeaddrinfo(res); } rtpproxy_disable = rtpp_test(0, 1); } else { rtpproxy_disable_tout = -1; } return 0;}static intisnulladdr(str *sx, int pf){ char *cp; if (pf == AF_INET6) { for(cp = sx->s; cp < sx->s + sx->len; cp++) if (*cp != '0' && *cp != ':') return 0; return 1; } return (sx->len == 7 && memcmp("0.0.0.0", sx->s, 7) == 0);}/* * ser_memmem() returns the location of the first occurrence of data * pattern b2 of size len2 in memory block b1 of size len1 or * NULL if none is found. Obtained from NetBSD. */static void *ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2){ /* Initialize search pointer */ char *sp = (char *) b1; /* Initialize pattern pointer */ char *pp = (char *) b2; /* Initialize end of search address space pointer */ char *eos = sp + len1 - len2; /* Sanity check */ if(!(b1 && b2 && len1 && len2)) return NULL; while (sp <= eos) { if (*sp == *pp) if (memcmp(sp, pp, len2) == 0) return sp; sp++; } return NULL;}/* * Some helper functions taken verbatim from tm module. *//* * Extract tag from To header field of a response * assumes the to header is already parsed, so * make sure it really is before calling this function */static inline intget_to_tag(struct sip_msg* _m, str* _tag){ if (!_m->to) { LOG(L_ERR, "get_to_tag(): To header field missing\n"); return -1; } if (get_to(_m)->tag_value.len) { _tag->s = get_to(_m)->tag_value.s; _tag->len = get_to(_m)->tag_value.len; } else { _tag->s = 0; /* fixes gcc 4.0 warnings */ _tag->len = 0; } return 0;}/* * Extract tag from From header field of a request */static inline intget_from_tag(struct sip_msg* _m, str* _tag){ if (parse_from_header(_m) == -1) { LOG(L_ERR, "get_from_tag(): Error while parsing From header\n"); return -1; } if (get_from(_m)->tag_value.len) { _tag->s = get_from(_m)->tag_value.s; _tag->len = get_from(_m)->tag_value.len; } else { _tag->len = 0; } return 0;}/* * Extract Call-ID value * assumes the callid header is already parsed * (so make sure it is, before calling this function or * it might fail even if the message _has_ a callid) */static inline intget_callid(struct sip_msg* _m, str* _cid){ if ((parse_headers(_m, HDR_CALLID, 0) == -1)) { LOG(L_ERR, "get_callid(): parse_headers() failed\n"); return -1; } if (_m->callid == NULL) { LOG(L_ERR, "get_callid(): Call-ID not found\n"); return -1; } _cid->s = _m->callid->body.s; _cid->len = _m->callid->body.len; trim(_cid); return 0;}/* * Extract URI from the Contact header field */static inline intget_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c){ if ((parse_headers(_m, HDR_CONTACT, 0) == -1) || !_m->contact) return -1; if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { LOG(L_ERR, "get_contact_uri: Error while parsing Contact body\n"); return -1; } *_c = ((contact_body_t*)_m->contact->parsed)->contacts; if (*_c == NULL) { LOG(L_ERR, "get_contact_uri: Error while parsing Contact body\n"); return -1; } if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { LOG(L_ERR, "get_contact_uri: Error while parsing Contact URI\n"); return -1; } return 0;}/* * Replaces ip:port pair in the Contact: field with the source address * of the packet. */static intfix_nated_contact_f(struct sip_msg* msg, char* str1, char* str2){ int offset, len, len1; char *cp, *buf, temp[2]; contact_t* c; struct lump* anchor; struct sip_uri uri; str hostport; if (get_contact_uri(msg, &uri, &c) == -1) return -1; if (uri.proto != PROTO_UDP && uri.proto != PROTO_NONE) return -1; if ((c->uri.s < msg->buf) || (c->uri.s > (msg->buf + msg->len))) { LOG(L_ERR, "ERROR: you can't call fix_nated_contact twice, " "check your config!\n"); return -1; } offset = c->uri.s - msg->buf; anchor = del_lump(msg, offset, c->uri.len, HDR_CONTACT); if (anchor == 0) return -1; hostport = uri.host; if (uri.port.len > 0) hostport.len = uri.port.s + uri.port.len - uri.host.s; cp = ip_addr2a(&msg->rcv.src_ip); len = c->uri.len + strlen(cp) + 6 /* :port */ - hostport.len + 1; buf = pkg_malloc(len); if (buf == NULL) { LOG(L_ERR, "ERROR: fix_nated_contact: out of memory\n"); return -1; } temp[0] = hostport.s[0]; temp[1] = c->uri.s[c->uri.len]; c->uri.s[c->uri.len] = hostport.s[0] = '\0'; len1 = snprintf(buf, len, "%s%s:%d%s", c->uri.s, cp, msg->rcv.src_port, hostport.s + hostport.len); if (len1 < len) len = len1; hostport.s[0] = temp[0]; c->uri.s[c->uri.len] = temp[1]; if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT) == 0) { pkg_free(buf); return -1; } c->uri.s = buf; c->uri.len = len; return 1;}inline static intfixup_str2int( void** param, int param_no){ unsigned long go_to;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -