|
@@ -1,1604 +0,0 @@
|
|
|
-/* $Id$
|
|
|
- *
|
|
|
- * Copyright (C) 2004 Dan Pascu
|
|
|
- *
|
|
|
- * 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:
|
|
|
- * [email protected]
|
|
|
- *
|
|
|
- * 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
|
|
|
- *
|
|
|
- */
|
|
|
-
|
|
|
-// TODO
|
|
|
-//
|
|
|
-// - make the asymmetric files use CFG_DIR
|
|
|
-// - find a way to install the config files with make install
|
|
|
-
|
|
|
-#include "../../sr_module.h"
|
|
|
-#include "../../dprint.h"
|
|
|
-#include "../../str.h"
|
|
|
-#include "../../error.h"
|
|
|
-#include "../../data_lump.h"
|
|
|
-#include "../../forward.h"
|
|
|
-#include "../../mem/mem.h"
|
|
|
-#include "../../resolve.h"
|
|
|
-#include "../../timer.h"
|
|
|
-#include "../../ut.h"
|
|
|
-#include "../../parser/parse_from.h"
|
|
|
-#include "../../parser/parse_to.h"
|
|
|
-#include "../../parser/parse_uri.h"
|
|
|
-#include "../../msg_translator.h"
|
|
|
-#include "../../id.h"
|
|
|
-#include "../registrar/sip_msg.h"
|
|
|
-#include "../usrloc/usrloc.h"
|
|
|
-
|
|
|
-#include <stdio.h>
|
|
|
-#include <stdlib.h>
|
|
|
-#include <unistd.h>
|
|
|
-#include <string.h>
|
|
|
-#include <ctype.h>
|
|
|
-#include <errno.h>
|
|
|
-#include <regex.h>
|
|
|
-#include <sys/types.h>
|
|
|
-#include <sys/stat.h>
|
|
|
-#include <sys/socket.h>
|
|
|
-#include <netinet/in.h>
|
|
|
-#include <arpa/inet.h>
|
|
|
-#include <sys/un.h>
|
|
|
-
|
|
|
-
|
|
|
-MODULE_VERSION
|
|
|
-
|
|
|
-
|
|
|
-// Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to
|
|
|
-// more systems. `AF_UNIX' was the traditional name stemming from BSD, so
|
|
|
-// even most POSIX systems support it. It is also the name of choice in
|
|
|
-// the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX
|
|
|
-#ifndef AF_LOCAL
|
|
|
-# define AF_LOCAL AF_UNIX
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-#define min(x, y) (((x) < (y)) ? (x) : (y))
|
|
|
-
|
|
|
-#define isAnyAddress(adr) ((adr).len==7 && memcmp("0.0.0.0", (adr).s, 7)==0)
|
|
|
-
|
|
|
-
|
|
|
-typedef int Bool;
|
|
|
-#define True 1
|
|
|
-#define False 0
|
|
|
-
|
|
|
-
|
|
|
-typedef int (*CheckLocalPartyProc)(struct sip_msg* msg, char* s1, char* s2);
|
|
|
-
|
|
|
-typedef Bool (*NatTestProc)(struct sip_msg* msg);
|
|
|
-
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- NTNone=0,
|
|
|
- NTPrivateContact=1,
|
|
|
- NTSourceAddress=2,
|
|
|
- NTPrivateVia=4
|
|
|
-} NatTestType;
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- STUnknown=0,
|
|
|
- STAudio,
|
|
|
- STVideo,
|
|
|
- STAudioVideo
|
|
|
-} StreamType;
|
|
|
-
|
|
|
-typedef struct {
|
|
|
- const char *name;
|
|
|
- uint32_t address;
|
|
|
- uint32_t mask;
|
|
|
-} NetInfo;
|
|
|
-
|
|
|
-typedef struct {
|
|
|
- str ip;
|
|
|
- str port;
|
|
|
- str type; // stream type (`audio', `video', ...)
|
|
|
- int localIP; // true if the IP is locally defined inside this media stream
|
|
|
-} StreamInfo;
|
|
|
-
|
|
|
-typedef struct {
|
|
|
- NatTestType test;
|
|
|
- NatTestProc proc;
|
|
|
-} NatTest;
|
|
|
-
|
|
|
-typedef struct {
|
|
|
- char *file; // the file which lists the asymmetric clients
|
|
|
- long timestamp; // for checking if it was modified
|
|
|
-
|
|
|
- regex_t **clients; // the asymmetric clients regular expressions
|
|
|
- int size; // size of array above
|
|
|
- int count; // how many clients are in array above
|
|
|
-} AsymmetricClients;
|
|
|
-
|
|
|
-
|
|
|
-/* Function prototypes */
|
|
|
-static int ClientNatTest(struct sip_msg *msg, char *str1, char *str2);
|
|
|
-static int FixContact(struct sip_msg *msg, char *str1, char *str2);
|
|
|
-static int UseMediaProxy(struct sip_msg *msg, char *str1, char *str2);
|
|
|
-static int EndMediaSession(struct sip_msg *msg, char *str1, char *str2);
|
|
|
-
|
|
|
-static int mod_init(void);
|
|
|
-
|
|
|
-static Bool testPrivateContact(struct sip_msg* msg);
|
|
|
-static Bool testSourceAddress(struct sip_msg* msg);
|
|
|
-static Bool testPrivateVia(struct sip_msg* msg);
|
|
|
-
|
|
|
-
|
|
|
-/* Local global variables */
|
|
|
-static char *mediaproxySocket = "/var/run/proxydispatcher.sock";
|
|
|
-
|
|
|
-static int natpingInterval = 60; // 60 seconds
|
|
|
-
|
|
|
-static usrloc_api_t userLocation;
|
|
|
-
|
|
|
-static AsymmetricClients sipAsymmetrics = {
|
|
|
- "/etc/ser/sip-asymmetric-clients",
|
|
|
- //CFG_DIR"/sip-asymmetric-clients",
|
|
|
- 0,
|
|
|
- NULL,
|
|
|
- 0,
|
|
|
- 0
|
|
|
-};
|
|
|
-
|
|
|
-static AsymmetricClients rtpAsymmetrics = {
|
|
|
- "/etc/ser/rtp-asymmetric-clients",
|
|
|
- 0,
|
|
|
- NULL,
|
|
|
- 0,
|
|
|
- 0
|
|
|
-};
|
|
|
-
|
|
|
-//static CheckLocalPartyProc isToLocal;
|
|
|
-
|
|
|
-NetInfo rfc1918nets[] = {
|
|
|
- {"10.0.0.0", 0x0a000000UL, 0xff000000UL},
|
|
|
- {"172.16.0.0", 0xac100000UL, 0xfff00000UL},
|
|
|
- {"192.168.0.0", 0xc0a80000UL, 0xffff0000UL},
|
|
|
- {NULL, 0UL, 0UL}
|
|
|
-};
|
|
|
-
|
|
|
-NatTest natTests[] = {
|
|
|
- {NTPrivateContact, testPrivateContact},
|
|
|
- {NTSourceAddress, testSourceAddress},
|
|
|
- {NTPrivateVia, testPrivateVia},
|
|
|
- {NTNone, NULL}
|
|
|
-};
|
|
|
-
|
|
|
-static cmd_export_t commands[] = {
|
|
|
- {"fix_contact", FixContact, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
|
|
|
- {"use_media_proxy", UseMediaProxy, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
|
|
|
- {"end_media_session", EndMediaSession, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
|
|
|
- {"client_nat_test", ClientNatTest, 1, fixup_int_1, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
|
|
|
- {0, 0, 0, 0, 0}
|
|
|
-};
|
|
|
-
|
|
|
-static param_export_t parameters[] = {
|
|
|
- {"mediaproxy_socket", PARAM_STRING, &mediaproxySocket},
|
|
|
- {"sip_asymmetrics", PARAM_STRING, &(sipAsymmetrics.file)},
|
|
|
- {"rtp_asymmetrics", PARAM_STRING, &(rtpAsymmetrics.file)},
|
|
|
- {"natping_interval", PARAM_INT, &natpingInterval},
|
|
|
- {0, 0, 0}
|
|
|
-};
|
|
|
-
|
|
|
-struct module_exports exports = {
|
|
|
- "mediaproxy", // module name
|
|
|
- commands, // module exported functions
|
|
|
- 0, // RPC methods
|
|
|
- parameters, // module exported parameters
|
|
|
- mod_init, // module init (before any kid is created. kids will inherit)
|
|
|
- NULL, // reply processing
|
|
|
- NULL, // destroy function
|
|
|
- NULL, // on_break
|
|
|
- NULL // child_init
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/* Helper functions */
|
|
|
-
|
|
|
-// Functions dealing with strings
|
|
|
-
|
|
|
-/*
|
|
|
- * strfind() finds the start of the first occurrence of the substring needle
|
|
|
- * of length nlen in the memory area haystack of length len.
|
|
|
- */
|
|
|
-static void*
|
|
|
-strfind(const void *haystack, size_t len, const void *needle, size_t nlen)
|
|
|
-{
|
|
|
- char *sp;
|
|
|
-
|
|
|
- /* Sanity check */
|
|
|
- if(!(haystack && needle && nlen && len>=nlen))
|
|
|
- return NULL;
|
|
|
-
|
|
|
- for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
|
|
|
- if (*sp == *(char*)needle && memcmp(sp, needle, nlen)==0) {
|
|
|
- return sp;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * strcasefind() finds the start of the first occurrence of the substring
|
|
|
- * needle of length nlen in the memory area haystack of length len by doing
|
|
|
- * a case insensitive search
|
|
|
- */
|
|
|
-static void*
|
|
|
-strcasefind(const char *haystack, size_t len, const char *needle, size_t nlen)
|
|
|
-{
|
|
|
- char *sp;
|
|
|
-
|
|
|
- /* Sanity check */
|
|
|
- if(!(haystack && needle && nlen && len>=nlen))
|
|
|
- return NULL;
|
|
|
-
|
|
|
- for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
|
|
|
- if (tolower(*sp) == tolower(*(char*)needle) &&
|
|
|
- strncasecmp(sp, needle, nlen)==0) {
|
|
|
- return sp;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-/* returns string with whitespace trimmed from left end */
|
|
|
-static inline void
|
|
|
-ltrim(str *string)
|
|
|
-{
|
|
|
- while (string->len>0 && isspace((int)*(string->s))) {
|
|
|
- string->len--;
|
|
|
- string->s++;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* returns string with whitespace trimmed from right end */
|
|
|
-static inline void
|
|
|
-rtrim(str *string)
|
|
|
-{
|
|
|
- char *ptr;
|
|
|
-
|
|
|
- ptr = string->s + string->len - 1;
|
|
|
- while (string->len>0 && (*ptr==0 || isspace((int)*ptr))) {
|
|
|
- string->len--;
|
|
|
- ptr--;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* returns string with whitespace trimmed from both ends */
|
|
|
-static inline void
|
|
|
-trim(str *string)
|
|
|
-{
|
|
|
- ltrim(string);
|
|
|
- rtrim(string);
|
|
|
-}
|
|
|
-
|
|
|
-/* returns a pointer to first CR or LF char found or the end of string */
|
|
|
-static char*
|
|
|
-findendline(char *string, int len)
|
|
|
-{
|
|
|
- char *ptr = string;
|
|
|
-
|
|
|
- while(ptr - string < len && *ptr != '\n' && *ptr != '\r')
|
|
|
- ptr++;
|
|
|
-
|
|
|
- return ptr;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-static int
|
|
|
-strtoint(str *data)
|
|
|
-{
|
|
|
- long int result;
|
|
|
- char c;
|
|
|
-
|
|
|
- // hack to avoid copying the string
|
|
|
- c = data->s[data->len];
|
|
|
- data->s[data->len] = 0;
|
|
|
- result = strtol(data->s, NULL, 10);
|
|
|
- data->s[data->len] = c;
|
|
|
-
|
|
|
- return (int)result;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Free the returned string with pkg_free() after you've done with it */
|
|
|
-static char*
|
|
|
-encodeQuopri(str buf)
|
|
|
-{
|
|
|
- char *result;
|
|
|
- int i, j;
|
|
|
- char c;
|
|
|
-
|
|
|
- result = pkg_malloc(buf.len*3+1);
|
|
|
- if (!result) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/encodeQuopri(): out of memory\n");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- for (i=0, j=0; i<buf.len; i++) {
|
|
|
- c = buf.s[i];
|
|
|
- if ((c>0x20 && c<0x7f && c!='=') || c=='\n' || c=='\r') {
|
|
|
- result[j++] = c;
|
|
|
- } else {
|
|
|
- result[j++] = '=';
|
|
|
- sprintf(&result[j], "%02X", (c & 0xff));
|
|
|
- j += 2;
|
|
|
- }
|
|
|
- }
|
|
|
- result[j] = 0;
|
|
|
-
|
|
|
- return result;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Find a line in str `block' that starts with `start'. */
|
|
|
-static char*
|
|
|
-findLineStartingWith(str *block, char *start, int ignoreCase)
|
|
|
-{
|
|
|
- char *ptr, *bend;
|
|
|
- str zone;
|
|
|
- int tlen;
|
|
|
-
|
|
|
- bend = block->s + block->len;
|
|
|
- tlen = strlen(start);
|
|
|
- ptr = NULL;
|
|
|
-
|
|
|
- for (zone = *block; zone.len > 0; zone.len = bend - zone.s) {
|
|
|
- if (ignoreCase)
|
|
|
- ptr = strcasefind(zone.s, zone.len, start, tlen);
|
|
|
- else
|
|
|
- ptr = strfind(zone.s, zone.len, start, tlen);
|
|
|
- if (!ptr || ptr==zone.s || ptr[-1]=='\n' || ptr[-1]=='\r')
|
|
|
- break;
|
|
|
- zone.s = ptr + tlen;
|
|
|
- }
|
|
|
-
|
|
|
- return ptr;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* get up to `limit' whitespace separated tokens from `char *string' */
|
|
|
-static int
|
|
|
-getTokens(char *string, str *tokens, int limit)
|
|
|
-{
|
|
|
- int i, len, size;
|
|
|
- char *ptr;
|
|
|
-
|
|
|
- if (!string) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- len = strlen(string);
|
|
|
-
|
|
|
- for (ptr=string, i=0; i<limit && len>0; i++) {
|
|
|
- size = strspn(ptr, " \t\n\r");
|
|
|
- ptr += size;
|
|
|
- len -= size;
|
|
|
- if (len <= 0)
|
|
|
- break;
|
|
|
- size = strcspn(ptr, " \t\n\r");
|
|
|
- if (size==0)
|
|
|
- break;
|
|
|
- tokens[i].s = ptr;
|
|
|
- tokens[i].len = size;
|
|
|
- ptr += size;
|
|
|
- len -= size;
|
|
|
- }
|
|
|
-
|
|
|
- return i;
|
|
|
-}
|
|
|
-
|
|
|
-/* get up to `limit' whitespace separated tokens from `str *string' */
|
|
|
-static int
|
|
|
-getStrTokens(str *string, str *tokens, int limit)
|
|
|
-{
|
|
|
- int count;
|
|
|
- char c;
|
|
|
-
|
|
|
- if (!string || !string->s) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- c = string->s[string->len];
|
|
|
- string->s[string->len] = 0;
|
|
|
-
|
|
|
- count = getTokens(string->s, tokens, limit);
|
|
|
-
|
|
|
- string->s[string->len] = c;
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Functions to extract the info we need from the SIP/SDP message */
|
|
|
-
|
|
|
-/* Extract Call-ID value. */
|
|
|
-static Bool
|
|
|
-getCallId(struct sip_msg* msg, str *cid)
|
|
|
-{
|
|
|
- if (msg->callid == NULL) {
|
|
|
- if (parse_headers(msg, HDR_CALLID_F, 0) == -1) {
|
|
|
- return False;
|
|
|
- }
|
|
|
- if (msg->callid == NULL) {
|
|
|
- return False;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- *cid = msg->callid->body;
|
|
|
-
|
|
|
- trim(cid);
|
|
|
-
|
|
|
- return True;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Get caller domain */
|
|
|
-static str
|
|
|
-getFromDomain(char** type, struct sip_msg* msg)
|
|
|
-{
|
|
|
- static char buf[16] = "unknown"; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 7}; // use the constant string directly!
|
|
|
- static struct sip_uri puri;
|
|
|
- str uri, did;
|
|
|
-
|
|
|
- if (get_from_did(&did, msg) == 1) {
|
|
|
- *type = "local";
|
|
|
- return did;
|
|
|
- }
|
|
|
-
|
|
|
- *type = "remote";
|
|
|
-
|
|
|
- if (parse_from_header(msg) < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getFromDomain(): error parsing `From' header\n");
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- uri = get_from(msg)->uri;
|
|
|
-
|
|
|
- if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getFromDomain(): error parsing `From' URI\n");
|
|
|
- return notfound;
|
|
|
- } else if (puri.host.len == 0) {
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- return puri.host;
|
|
|
-}
|
|
|
-
|
|
|
-/* Get called domain */
|
|
|
-static str
|
|
|
-getToDomain(char** type, struct sip_msg* msg)
|
|
|
-{
|
|
|
- static char buf[16] = "unknown"; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 7}; // use the constant string directly!
|
|
|
- static struct sip_uri puri;
|
|
|
- str uri, did;
|
|
|
-
|
|
|
- if (get_to_did(&did, msg) == 0) {
|
|
|
- *type = "local";
|
|
|
- return did;
|
|
|
- }
|
|
|
-
|
|
|
- *type = "remote";
|
|
|
-
|
|
|
- uri = get_to(msg)->uri;
|
|
|
-
|
|
|
- if (parse_uri(uri.s, uri.len, &puri) < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getToDomain(): error parsing `To' URI\n");
|
|
|
- return notfound;
|
|
|
- } else if (puri.host.len == 0) {
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- return puri.host;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-/* Get destination domain */
|
|
|
-// This function only works when called for a request although it's more
|
|
|
-// reliable than the getToDomain function.
|
|
|
-static str
|
|
|
-getDestinationDomain(char** type, struct sip_msg* msg)
|
|
|
-{
|
|
|
- static char buf[16] = "unknown"; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 7}; // use the constant string directly!
|
|
|
- str did;
|
|
|
-
|
|
|
- if (get_to_did(&did, msg) == 0) {
|
|
|
- *type = "local";
|
|
|
- return did;
|
|
|
- }
|
|
|
-
|
|
|
- *type = "remote";
|
|
|
-
|
|
|
- if (parse_sip_msg_uri(msg) < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getDestinationDomain(): error parsing destination URI\n");
|
|
|
- return notfound;
|
|
|
- } else if (msg->parsed_uri.host.len==0) {
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- return msg->parsed_uri.host;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Get From tag */
|
|
|
-static str
|
|
|
-getFromAddress(struct sip_msg *msg)
|
|
|
-{
|
|
|
- static char buf[16] = "unknown"; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 7}; // use the constant string directly!
|
|
|
- str uri;
|
|
|
- char *ptr;
|
|
|
-
|
|
|
- if (parse_from_header(msg) == -1) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getFromAddress(): error parsing From: field\n");
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- uri = get_from(msg)->uri;
|
|
|
-
|
|
|
- if (uri.len == 0)
|
|
|
- return notfound;
|
|
|
-
|
|
|
- if (strncmp(uri.s, "sip:", 4)==0) {
|
|
|
- uri.s += 4;
|
|
|
- uri.len -= 4;
|
|
|
- }
|
|
|
-
|
|
|
- if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
|
|
|
- uri.len = ptr - uri.s;
|
|
|
- }
|
|
|
-
|
|
|
- return uri;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Get To tag */
|
|
|
-static str
|
|
|
-getToAddress(struct sip_msg *msg)
|
|
|
-{
|
|
|
- static char buf[16] = "unknown"; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 7}; // use the constant string directly!
|
|
|
- str uri;
|
|
|
- char *ptr;
|
|
|
-
|
|
|
- if (!msg->to) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getToAddress(): missing To: field\n");
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- uri = get_to(msg)->uri;
|
|
|
-
|
|
|
- if (uri.len == 0)
|
|
|
- return notfound;
|
|
|
-
|
|
|
- if (strncmp(uri.s, "sip:", 4)==0) {
|
|
|
- uri.s += 4;
|
|
|
- uri.len -= 4;
|
|
|
- }
|
|
|
-
|
|
|
- if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
|
|
|
- uri.len = ptr - uri.s;
|
|
|
- }
|
|
|
-
|
|
|
- return uri;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Get From tag */
|
|
|
-static str
|
|
|
-getFromTag(struct sip_msg *msg)
|
|
|
-{
|
|
|
- static char buf[4] = ""; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 0}; // use the constant string directly!
|
|
|
- str tag;
|
|
|
-
|
|
|
- if (parse_from_header(msg) == -1) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getFromTag(): error parsing From: field\n");
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- tag = get_from(msg)->tag_value;
|
|
|
-
|
|
|
- if (tag.len == 0)
|
|
|
- return notfound;
|
|
|
-
|
|
|
- return tag;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Get To tag */
|
|
|
-static str
|
|
|
-getToTag(struct sip_msg *msg)
|
|
|
-{
|
|
|
- static char buf[4] = ""; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 0}; // use the constant string directly!
|
|
|
- str tag;
|
|
|
-
|
|
|
- if (!msg->to) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getToTag(): missing To: field\n");
|
|
|
- return notfound;
|
|
|
- }
|
|
|
-
|
|
|
- tag = get_to(msg)->tag_value;
|
|
|
-
|
|
|
- if (tag.len == 0)
|
|
|
- return notfound;
|
|
|
-
|
|
|
- return tag;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Extract User-Agent */
|
|
|
-static str
|
|
|
-getUserAgent(struct sip_msg* msg)
|
|
|
-{
|
|
|
- static char buf[16] = "unknown-agent"; // buf is here for a reason. don't
|
|
|
- static str notfound = {buf, 13}; // use the constant string directly!
|
|
|
- str block, server;
|
|
|
- char *ptr;
|
|
|
-
|
|
|
- if ((parse_headers(msg, HDR_USERAGENT_F, 0)!=-1) && msg->user_agent &&
|
|
|
- msg->user_agent->body.len>0) {
|
|
|
- return msg->user_agent->body;
|
|
|
- }
|
|
|
-
|
|
|
- // If we can't find user-agent, look after the Server: field
|
|
|
-
|
|
|
- // This is a temporary hack. Normally it should be extracted by ser
|
|
|
- // (either as the Server field, or if User-Agent is missing in place
|
|
|
- // of the User-Agent field)
|
|
|
-
|
|
|
- block.s = msg->buf;
|
|
|
- block.len = msg->len;
|
|
|
-
|
|
|
- ptr = findLineStartingWith(&block, "Server:", True);
|
|
|
- if (!ptr)
|
|
|
- return notfound;
|
|
|
-
|
|
|
- server.s = ptr + 7;
|
|
|
- server.len = findendline(server.s, block.s+block.len-server.s) - server.s;
|
|
|
-
|
|
|
- trim(&server);
|
|
|
- if (server.len == 0)
|
|
|
- return notfound;
|
|
|
-
|
|
|
- return server;
|
|
|
-}
|
|
|
-
|
|
|
-// Get URI from the Contact: field.
|
|
|
-static Bool
|
|
|
-getContactURI(struct sip_msg* msg, struct sip_uri *uri, contact_t** _c)
|
|
|
-{
|
|
|
-
|
|
|
- if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact)
|
|
|
- return False;
|
|
|
-
|
|
|
- if (!msg->contact->parsed && parse_contact(msg->contact) < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getContactURI(): cannot parse Contact header\n");
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- *_c = ((contact_body_t*)msg->contact->parsed)->contacts;
|
|
|
-
|
|
|
- if (*_c == NULL) {
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getContactURI(): cannot parse Contact URI\n");
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- return True;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// Functions to manipulate the SDP message body
|
|
|
-
|
|
|
-static Bool
|
|
|
-checkContentType(struct sip_msg *msg)
|
|
|
-{
|
|
|
- str type;
|
|
|
-
|
|
|
- if (!msg->content_type) {
|
|
|
- LOG(L_WARN, "warning: mediaproxy/checkContentType(): Content-Type "
|
|
|
- "header missing! Let's assume the content is text/plain ;-)\n");
|
|
|
- return True;
|
|
|
- }
|
|
|
-
|
|
|
- type = msg->content_type->body;
|
|
|
- trim(&type);
|
|
|
-
|
|
|
- if (strncasecmp(type.s, "application/sdp", 15) != 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/checkContentType(): invalid Content-Type "
|
|
|
- "for SDP message\n");
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- if (!(isspace((int)type.s[15]) || type.s[15] == ';' || type.s[15] == 0)) {
|
|
|
- LOG(L_ERR,"error: mediaproxy/checkContentType(): invalid character "
|
|
|
- "after Content-Type!\n");
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- return True;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// Get the SDP message from SIP message and check it's Content-Type
|
|
|
-// return -1 on error, 0 if empty message, 1 if message present and not empty
|
|
|
-static int
|
|
|
-getSDPMessage(struct sip_msg *msg, str *sdp)
|
|
|
-{
|
|
|
- sdp->s = get_body(msg);
|
|
|
- if (sdp->s==NULL) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getSDPMessage(): cannot get the SDP body from SIP message\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- sdp->len = msg->buf + msg->len - sdp->s;
|
|
|
- if (sdp->len==0) {
|
|
|
- // 0 length body is ok for ACK messages
|
|
|
- if (!(msg->first_line.type == SIP_REQUEST &&
|
|
|
- msg->first_line.u.request.method_value == METHOD_ACK)) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getSDPMessage(): SDP message has zero length\n");
|
|
|
- }
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- if (!checkContentType(msg)) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getSDPMessage(): content type is not `application/sdp'\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// will return the ip address present in a `c=' line in the given block
|
|
|
-// returns: -1 on error, 0 if not found, 1 if found
|
|
|
-static int
|
|
|
-getMediaIPFromBlock(str *block, str *mediaip)
|
|
|
-{
|
|
|
- str tokens[3], zone;
|
|
|
- char *ptr;
|
|
|
- int count;
|
|
|
-
|
|
|
- ptr = findLineStartingWith(block, "c=", False);
|
|
|
-
|
|
|
- if (!ptr) {
|
|
|
- mediaip->s = NULL;
|
|
|
- mediaip->len = 0;
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- zone.s = ptr + 2;
|
|
|
- zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
|
|
|
-
|
|
|
- count = getStrTokens(&zone, tokens, 3);
|
|
|
-
|
|
|
- if (count != 3) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getMediaIPFromBlock(): invalid `c=' "
|
|
|
- "line in SDP body\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- // can also check if tokens[1] == 'IP4'
|
|
|
- *mediaip = tokens[2];
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// Get the session-level media IP defined by the SDP message
|
|
|
-static Bool
|
|
|
-getSessionLevelMediaIP(str *sdp, str *mediaip)
|
|
|
-{
|
|
|
- str block;
|
|
|
- char *ptr;
|
|
|
-
|
|
|
- // session IP can be found from the beginning up to the first media block
|
|
|
- ptr = findLineStartingWith(sdp, "m=", False);
|
|
|
- if (ptr) {
|
|
|
- block.s = sdp->s;
|
|
|
- block.len = ptr - block.s;
|
|
|
- } else {
|
|
|
- block = *sdp;
|
|
|
- }
|
|
|
-
|
|
|
- if (getMediaIPFromBlock(&block, mediaip) == -1) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getSessionLevelMediaIP(): parse error "
|
|
|
- "while getting session-level media IP from SDP body\n");
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- // it's not an error to be missing. it can be locally defined
|
|
|
- // by each media stream. thus we return true even if not found
|
|
|
- return True;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// will get all media streams
|
|
|
-static int
|
|
|
-getMediaStreams(str *sdp, str *sessionIP, StreamInfo *streams, int limit)
|
|
|
-{
|
|
|
- str tokens[2], block, zone;
|
|
|
- char *ptr, *sdpEnd;
|
|
|
- int i, count, streamCount, result;
|
|
|
-
|
|
|
- sdpEnd = sdp->s + sdp->len;
|
|
|
-
|
|
|
- for (i=0, block=*sdp; i<limit; i++) {
|
|
|
- ptr = findLineStartingWith(&block, "m=", False);
|
|
|
-
|
|
|
- if (!ptr)
|
|
|
- break;
|
|
|
-
|
|
|
- zone.s = ptr + 2;
|
|
|
- zone.len = findendline(zone.s, sdpEnd - zone.s) - zone.s;
|
|
|
- count = getStrTokens(&zone, tokens, 2);
|
|
|
-
|
|
|
- if (count != 2) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getMediaStreams(): invalid `m=' "
|
|
|
- "line in SDP body\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- streams[i].type = tokens[0];
|
|
|
- streams[i].port = tokens[1];
|
|
|
-
|
|
|
- block.s = zone.s + zone.len;
|
|
|
- block.len = sdpEnd - block.s;
|
|
|
- }
|
|
|
-
|
|
|
- streamCount = i;
|
|
|
-
|
|
|
- for (i=0; i<streamCount; i++) {
|
|
|
- block.s = streams[i].port.s;
|
|
|
- if (i < streamCount-1)
|
|
|
- block.len = streams[i+1].port.s - block.s;
|
|
|
- else
|
|
|
- block.len = sdpEnd - block.s;
|
|
|
-
|
|
|
- result = getMediaIPFromBlock(&block, &(streams[i].ip));
|
|
|
- if (result == -1) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getMediaStreams(): parse error in "
|
|
|
- "getting the contact IP for the media stream nr. %d\n", i+1);
|
|
|
- return -1;
|
|
|
- } else if (result == 0) {
|
|
|
- if (sessionIP->s == NULL) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/getMediaStreams(): media stream "
|
|
|
- "doesn't define a contact IP and the session-level IP "
|
|
|
- "is missing\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- streams[i].ip = *sessionIP;
|
|
|
- streams[i].localIP = 0;
|
|
|
- } else {
|
|
|
- streams[i].localIP = 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return streamCount;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-static Bool
|
|
|
-replaceElement(struct sip_msg *msg, str *oldElem, str *newElem)
|
|
|
-{
|
|
|
- struct lump* anchor;
|
|
|
- char *buf;
|
|
|
-
|
|
|
- if (newElem->len==oldElem->len &&
|
|
|
- memcmp(newElem->s, oldElem->s, newElem->len)==0) {
|
|
|
- return True;
|
|
|
- }
|
|
|
-
|
|
|
- buf = pkg_malloc(newElem->len);
|
|
|
- if (!buf) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/replaceElement(): out of memory\n");
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- anchor = del_lump(msg, oldElem->s - msg->buf, oldElem->len, 0);
|
|
|
- if (!anchor) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/replaceElement(): failed to delete old element\n");
|
|
|
- pkg_free(buf);
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(buf, newElem->s, newElem->len);
|
|
|
-
|
|
|
- if (insert_new_lump_after(anchor, buf, newElem->len, 0)==0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/replaceElement(): failed to insert new element\n");
|
|
|
- pkg_free(buf);
|
|
|
- return False;
|
|
|
- }
|
|
|
-
|
|
|
- return True;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// Functions dealing with the external mediaproxy helper
|
|
|
-
|
|
|
-static inline size_t
|
|
|
-uwrite(int fd, const void *buf, size_t count)
|
|
|
-{
|
|
|
- int len;
|
|
|
-
|
|
|
- do
|
|
|
- len = write(fd, buf, count);
|
|
|
- while (len == -1 && errno == EINTR);
|
|
|
-
|
|
|
- return len;
|
|
|
-}
|
|
|
-
|
|
|
-static inline size_t
|
|
|
-uread(int fd, void *buf, size_t count)
|
|
|
-{
|
|
|
- int len;
|
|
|
-
|
|
|
- do
|
|
|
- len = read(fd, buf, count);
|
|
|
- while (len == -1 && errno == EINTR);
|
|
|
-
|
|
|
- return len;
|
|
|
-}
|
|
|
-
|
|
|
-static inline size_t
|
|
|
-readall(int fd, void *buf, size_t count)
|
|
|
-{
|
|
|
- int len, total;
|
|
|
-
|
|
|
- for (len=0, total=0; count-total>0; total+=len) {
|
|
|
- len = uread(fd, (char*)buf+total, count-total);
|
|
|
- if (len == -1) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (len == 0) {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return total;
|
|
|
-}
|
|
|
-
|
|
|
-static char*
|
|
|
-sendMediaproxyCommand(char *command)
|
|
|
-{
|
|
|
- struct sockaddr_un addr;
|
|
|
- int smpSocket, len;
|
|
|
- static char buf[1024];
|
|
|
-
|
|
|
- memset(&addr, 0, sizeof(addr));
|
|
|
- addr.sun_family = AF_LOCAL;
|
|
|
- strncpy(addr.sun_path, mediaproxySocket, sizeof(addr.sun_path) - 1);
|
|
|
-#ifdef HAVE_SOCKADDR_SA_LEN
|
|
|
- addr.sun_len = strlen(addr.sun_path);
|
|
|
-#endif
|
|
|
-
|
|
|
- smpSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
|
|
|
- if (smpSocket < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't create socket\n");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- if (connect(smpSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
|
|
- close(smpSocket);
|
|
|
- LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't connect to MediaProxy\n");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- len = uwrite(smpSocket, command, strlen(command));
|
|
|
-
|
|
|
- if (len <= 0) {
|
|
|
- close(smpSocket);
|
|
|
- LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't send command to MediaProxy\n");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- len = readall(smpSocket, buf, sizeof(buf)-1);
|
|
|
-
|
|
|
- close(smpSocket);
|
|
|
-
|
|
|
- if (len < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't read reply from MediaProxy\n");
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- buf[len] = 0;
|
|
|
-
|
|
|
- return buf;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// Miscellaneous helper functions
|
|
|
-
|
|
|
-/* Test if IP in `address' belongs to a RFC1918 network */
|
|
|
-static inline int
|
|
|
-rfc1918address(str *address)
|
|
|
-{
|
|
|
- struct in_addr inaddr;
|
|
|
- uint32_t netaddr;
|
|
|
- int i, result;
|
|
|
- char c;
|
|
|
-
|
|
|
- c = address->s[address->len];
|
|
|
- address->s[address->len] = 0;
|
|
|
-
|
|
|
- result = inet_aton(address->s, &inaddr);
|
|
|
-
|
|
|
- address->s[address->len] = c;
|
|
|
-
|
|
|
- if (result==0)
|
|
|
- return -1; /* invalid address to test */
|
|
|
-
|
|
|
- netaddr = ntohl(inaddr.s_addr);
|
|
|
-
|
|
|
- for (i=0; rfc1918nets[i].name!=NULL; i++) {
|
|
|
- if ((netaddr & rfc1918nets[i].mask)==rfc1918nets[i].address) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-#define isPrivateAddress(x) (rfc1918address(x)==1 ? 1 : 0)
|
|
|
-// test for a public address is more complex (also need to test for
|
|
|
-// address not in 0.0.0.0/8, 127.0.0.0/8, 224.0.0.0/4).
|
|
|
-// #define isPublicAddress(x) (rfc1918address(x)==0 ? 1 : 0)
|
|
|
-
|
|
|
-
|
|
|
-// Check if the requested asymmetrics file has changed and reload it if needed
|
|
|
-static void
|
|
|
-checkAsymmetricFile(AsymmetricClients *aptr)
|
|
|
-{
|
|
|
- char buf[512], errbuf[256], *which;
|
|
|
- regex_t *re, **regs;
|
|
|
- int i, size, code;
|
|
|
- Bool firstTime = False;
|
|
|
- struct stat statbuf;
|
|
|
- FILE *file;
|
|
|
- str line;
|
|
|
-
|
|
|
- if (stat(aptr->file, &statbuf) < 0)
|
|
|
- return; // ignore missing file
|
|
|
-
|
|
|
- if (statbuf.st_mtime <= aptr->timestamp)
|
|
|
- return; // not changed
|
|
|
-
|
|
|
- // now we have work to do
|
|
|
-
|
|
|
- which = (aptr == &sipAsymmetrics ? "SIP" : "RTP");
|
|
|
-
|
|
|
- if (!aptr->clients) {
|
|
|
- // if we are here the first time allocate memory to hold the regexps
|
|
|
-
|
|
|
- // initial size of array
|
|
|
- // (hopefully not reached so we won't need to realloc)
|
|
|
- size = 32;
|
|
|
- aptr->clients = (regex_t**)pkg_malloc(size*sizeof(regex_t**));
|
|
|
- if (!aptr->clients) {
|
|
|
- LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile() cannot "
|
|
|
- "allocate memory for the %s asymmetric client list. "
|
|
|
- "%s asymmetric clients will not be handled properly.\n",
|
|
|
- which, which);
|
|
|
- return; // ignore as it is not fatal
|
|
|
- }
|
|
|
- aptr->size = size;
|
|
|
- aptr->count = 0;
|
|
|
- firstTime = True;
|
|
|
- } else {
|
|
|
- // else clean old stuff
|
|
|
- for (i=0; i<aptr->count; i++) {
|
|
|
- regfree(aptr->clients[i]);
|
|
|
- pkg_free(aptr->clients[i]);
|
|
|
- aptr->clients[i] = NULL;
|
|
|
- }
|
|
|
- aptr->count = 0;
|
|
|
- }
|
|
|
-
|
|
|
- // read new
|
|
|
- file = fopen(aptr->file, "r");
|
|
|
- i = 0; // this will count on which line are we in the file
|
|
|
- while (!feof(file)) {
|
|
|
- if (!fgets(buf, 512, file))
|
|
|
- break;
|
|
|
- i++;
|
|
|
-
|
|
|
- line.s = buf;
|
|
|
- line.len = strlen(buf);
|
|
|
- trim(&line);
|
|
|
- if (line.len == 0 || line.s[0] == '#')
|
|
|
- continue; // comment or empty line. ignore
|
|
|
- line.s[line.len] = 0;
|
|
|
-
|
|
|
- re = (regex_t*)pkg_malloc(sizeof(regex_t));
|
|
|
- if (!re) {
|
|
|
- LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile(): "
|
|
|
- "cannot allocate memory for all the %s asymmetric "
|
|
|
- "clients listed in file. Some of them will not be "
|
|
|
- "handled properly.\n", which);
|
|
|
- break;
|
|
|
- }
|
|
|
- code = regcomp(re, line.s, REG_EXTENDED|REG_ICASE|REG_NOSUB);
|
|
|
- if (code == 0) {
|
|
|
- if (aptr->count+1 > aptr->size) {
|
|
|
- size = aptr->size * 2;
|
|
|
- regs = aptr->clients;
|
|
|
- regs = (regex_t**)pkg_realloc(regs, size*sizeof(regex_t**));
|
|
|
- if (!regs) {
|
|
|
- LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile(): "
|
|
|
- "cannot allocate memory for all the %s asymmetric "
|
|
|
- "clients listed in file. Some of them will not be "
|
|
|
- "handled properly.\n", which);
|
|
|
- break;
|
|
|
- }
|
|
|
- aptr->clients = regs;
|
|
|
- aptr->size = size;
|
|
|
- }
|
|
|
- aptr->clients[aptr->count] = re;
|
|
|
- aptr->count++;
|
|
|
- } else {
|
|
|
- regerror(code, re, errbuf, 256);
|
|
|
- LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile(): cannot "
|
|
|
- "compile line %d of the %s asymmetric clients file into a "
|
|
|
- "regular expression (will be ignored): %s", i, which, errbuf);
|
|
|
- pkg_free(re);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- aptr->timestamp = statbuf.st_mtime;
|
|
|
-
|
|
|
- LOG(L_INFO, "info: mediaproxy: %sloaded %s asymmetric clients file "
|
|
|
- "containing %d entr%s.\n", firstTime ? "" : "re",
|
|
|
- which, aptr->count, aptr->count==1 ? "y" : "ies");
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-//
|
|
|
-// This is a hack. Until a I find a better way to allow all children to update
|
|
|
-// the asymmetric client list when the files change on disk, it stays as it is
|
|
|
-// A timer registered from mod_init() only runs in one of the ser processes
|
|
|
-// and the others will always see the file that was read at startup.
|
|
|
-//
|
|
|
-#include <time.h>
|
|
|
-#define CHECK_INTERVAL 5
|
|
|
-static void
|
|
|
-periodicAsymmetricsCheck(void)
|
|
|
-{
|
|
|
- static time_t last = 0;
|
|
|
- time_t now;
|
|
|
-
|
|
|
- // this is not guaranteed to run at every CHECK_INTERVAL.
|
|
|
- // it is only guaranteed that the files won't be checked more often.
|
|
|
- now = time(NULL);
|
|
|
- if (now > last + CHECK_INTERVAL) {
|
|
|
- checkAsymmetricFile(&sipAsymmetrics);
|
|
|
- checkAsymmetricFile(&rtpAsymmetrics);
|
|
|
- last = now;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static Bool
|
|
|
-isSIPAsymmetric(str userAgent)
|
|
|
-{
|
|
|
- int i, code;
|
|
|
- char c;
|
|
|
-
|
|
|
- periodicAsymmetricsCheck();
|
|
|
-
|
|
|
- if (!sipAsymmetrics.clients || sipAsymmetrics.count==0)
|
|
|
- return False;
|
|
|
-
|
|
|
- c = userAgent.s[userAgent.len];
|
|
|
- userAgent.s[userAgent.len] = 0;
|
|
|
-
|
|
|
- for (i=0; i<sipAsymmetrics.count; i++) {
|
|
|
- code = regexec(sipAsymmetrics.clients[i], userAgent.s, 0, NULL, 0);
|
|
|
- if (code == 0) {
|
|
|
- userAgent.s[userAgent.len] = c;
|
|
|
- return True;
|
|
|
- } else if (code != REG_NOMATCH) {
|
|
|
- char errbuf[256];
|
|
|
- regerror(code, sipAsymmetrics.clients[i], errbuf, 256);
|
|
|
- LOG(L_WARN, "warning: mediaproxy/isSIPAsymmetric() failed to "
|
|
|
- "match regexp: %s\n", errbuf);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- userAgent.s[userAgent.len] = c;
|
|
|
-
|
|
|
- return False;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-static Bool
|
|
|
-isRTPAsymmetric(str userAgent)
|
|
|
-{
|
|
|
- int i, code;
|
|
|
- char c;
|
|
|
-
|
|
|
- periodicAsymmetricsCheck();
|
|
|
-
|
|
|
- if (!rtpAsymmetrics.clients || rtpAsymmetrics.count==0)
|
|
|
- return False;
|
|
|
-
|
|
|
- c = userAgent.s[userAgent.len];
|
|
|
- userAgent.s[userAgent.len] = 0;
|
|
|
-
|
|
|
- for (i=0; i<rtpAsymmetrics.count; i++) {
|
|
|
- code = regexec(rtpAsymmetrics.clients[i], userAgent.s, 0, NULL, 0);
|
|
|
- if (code == 0) {
|
|
|
- userAgent.s[userAgent.len] = c;
|
|
|
- return True;
|
|
|
- } else if (code != REG_NOMATCH) {
|
|
|
- char errbuf[256];
|
|
|
- regerror(code, rtpAsymmetrics.clients[i], errbuf, 256);
|
|
|
- LOG(L_WARN, "warning: mediaproxy/isRTPAsymmetric() failed to "
|
|
|
- "match regexp: %s\n", errbuf);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- userAgent.s[userAgent.len] = c;
|
|
|
-
|
|
|
- return False;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// NAT tests
|
|
|
-
|
|
|
-/* tests if address of signaling is different from address in 1st Via field */
|
|
|
-static Bool
|
|
|
-testSourceAddress(struct sip_msg* msg)
|
|
|
-{
|
|
|
- Bool diffIP, diffPort;
|
|
|
- int via1Port;
|
|
|
-
|
|
|
- diffIP = received_test(msg);
|
|
|
- if (isSIPAsymmetric(getUserAgent(msg))) {
|
|
|
- // ignore port test for asymmetric clients (it's always different)
|
|
|
- diffPort = False;
|
|
|
- } else {
|
|
|
- via1Port = (msg->via1->port ? msg->via1->port : SIP_PORT);
|
|
|
- diffPort = (msg->rcv.src_port != via1Port);
|
|
|
- }
|
|
|
-
|
|
|
- return (diffIP || diffPort);
|
|
|
-}
|
|
|
-
|
|
|
-/* tests if Contact field contains a private IP address as defined in RFC1918 */
|
|
|
-static Bool
|
|
|
-testPrivateContact(struct sip_msg* msg)
|
|
|
-{
|
|
|
- struct sip_uri uri;
|
|
|
- contact_t* contact;
|
|
|
-
|
|
|
- if (!getContactURI(msg, &uri, &contact))
|
|
|
- return False;
|
|
|
-
|
|
|
- return isPrivateAddress(&(uri.host));
|
|
|
-}
|
|
|
-
|
|
|
-/* tests if top Via field contains a private IP address as defined in RFC1918 */
|
|
|
-static Bool
|
|
|
-testPrivateVia(struct sip_msg* msg)
|
|
|
-{
|
|
|
- return isPrivateAddress(&(msg->via1->host));
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-#include "functions.h"
|
|
|
-
|
|
|
-/* The public functions that are exported by this module */
|
|
|
-
|
|
|
-static int
|
|
|
-ClientNatTest(struct sip_msg* msg, char* str1, char* str2)
|
|
|
-{
|
|
|
- int tests, i;
|
|
|
-
|
|
|
- if (get_int_fparam(&tests, msg, (fparam_t*) str1) < 0) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- for (i=0; natTests[i].test!=NTNone; i++) {
|
|
|
- if ((tests & natTests[i].test)!=0 && natTests[i].proc(msg)) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return -1; // all failed
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-static int
|
|
|
-EndMediaSession(struct sip_msg* msg, char* str1, char* str2)
|
|
|
-{
|
|
|
- char *command, *result;
|
|
|
- str callId;
|
|
|
-
|
|
|
- if (!getCallId(msg, &callId)) {
|
|
|
- LOG(L_ERR, "error: end_media_session(): can't get Call-Id\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- command = pkg_malloc(callId.len + 20);
|
|
|
- if (command == NULL) {
|
|
|
- LOG(L_ERR, "error: end_media_session(): out of memory\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- sprintf(command, "delete %.*s info=\n", callId.len, callId.s);
|
|
|
- result = sendMediaproxyCommand(command);
|
|
|
-
|
|
|
- pkg_free(command);
|
|
|
-
|
|
|
- return result==NULL ? -1 : 1;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-#define MSG_UNKNOWN 0
|
|
|
-#define MSG_INVITE 1
|
|
|
-#define MSG_ACK 2
|
|
|
-#define MSG_REPLY 3
|
|
|
-
|
|
|
-
|
|
|
-static int
|
|
|
-UseMediaProxy(struct sip_msg* msg, char* str1, char* str2)
|
|
|
-{
|
|
|
- str sdp, sessionIP, callId, fromDomain, toDomain, userAgent, tokens[64];
|
|
|
- str fromAddr, toAddr, fromTag, toTag;
|
|
|
- char *clientIP, *ptr, *command, *result, *agent, *fromType, *toType, *info;
|
|
|
- int streamCount, i, port, count, portCount, cmdlen, infolen, success, type;
|
|
|
- StreamInfo streams[64], stream;
|
|
|
- Bool request;
|
|
|
-
|
|
|
- if (msg->first_line.type == SIP_REQUEST) {
|
|
|
- if (msg->first_line.u.request.method_value == METHOD_INVITE)
|
|
|
- type = MSG_INVITE;
|
|
|
- else if (msg->first_line.u.request.method_value == METHOD_ACK)
|
|
|
- type = MSG_ACK;
|
|
|
- else
|
|
|
- type = MSG_UNKNOWN;
|
|
|
- } else if (msg->first_line.type == SIP_REPLY) {
|
|
|
- type = MSG_REPLY;
|
|
|
- } else {
|
|
|
- type = MSG_UNKNOWN;
|
|
|
- }
|
|
|
-
|
|
|
- if (type==MSG_INVITE || type==MSG_ACK) {
|
|
|
- request = True;
|
|
|
- } else if (type==MSG_REPLY) {
|
|
|
- request = False;
|
|
|
- } else {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if (!getCallId(msg, &callId)) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): can't get Call-Id\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- success = getSDPMessage(msg, &sdp);
|
|
|
- if (success==0 && type==MSG_ACK) {
|
|
|
- return 1; // nothing to do. it's ok for ACK to not have a SDP body
|
|
|
- } else if (success <= 0) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): failed to get the SDP message\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if (!getSessionLevelMediaIP(&sdp, &sessionIP)) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): error parsing the SDP message\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- streamCount = getMediaStreams(&sdp, &sessionIP, streams, 64);
|
|
|
- if (streamCount == -1) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): can't extract media streams "
|
|
|
- "from the SDP message\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if (streamCount == 0) {
|
|
|
- // there are no media streams. we have nothing to do.
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
- fromDomain = getFromDomain(&fromType, msg);
|
|
|
- fromAddr = getFromAddress(msg);
|
|
|
- toAddr = getToAddress(msg);
|
|
|
- fromTag = getFromTag(msg);
|
|
|
- toTag = getToTag(msg);
|
|
|
- userAgent = getUserAgent(msg);
|
|
|
- if (request) {
|
|
|
- toDomain = getDestinationDomain(&toType, msg); // call only for requests
|
|
|
- } else {
|
|
|
- toDomain = getToDomain(&toType, msg);
|
|
|
- }
|
|
|
-
|
|
|
- clientIP = ip_addr2a(&msg->rcv.src_ip);
|
|
|
-
|
|
|
- infolen = fromAddr.len + toAddr.len + fromTag.len + toTag.len + 64;
|
|
|
-
|
|
|
- cmdlen = callId.len + strlen(clientIP) + fromDomain.len + toDomain.len +
|
|
|
- userAgent.len*3 + infolen + 128;
|
|
|
-
|
|
|
- for (i=0; i<streamCount; i++) {
|
|
|
- stream = streams[i];
|
|
|
- cmdlen += stream.ip.len + stream.port.len + stream.type.len + 4;
|
|
|
- }
|
|
|
-
|
|
|
- command = pkg_malloc(cmdlen);
|
|
|
- if (!command) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): out of memory\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if (request)
|
|
|
- count = sprintf(command, "request %.*s", callId.len, callId.s);
|
|
|
- else
|
|
|
- count = sprintf(command, "lookup %.*s", callId.len, callId.s);
|
|
|
-
|
|
|
- for (i=0, ptr=command+count; i<streamCount; i++) {
|
|
|
- char c = (i==0 ? ' ' : ',');
|
|
|
- count = sprintf(ptr, "%c%.*s:%.*s:%.*s", c,
|
|
|
- streams[i].ip.len, streams[i].ip.s,
|
|
|
- streams[i].port.len, streams[i].port.s,
|
|
|
- streams[i].type.len, streams[i].type.s);
|
|
|
- ptr += count;
|
|
|
- }
|
|
|
-
|
|
|
- agent = encodeQuopri(userAgent);
|
|
|
- if (!agent) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): out of memory\n");
|
|
|
- pkg_free(command);
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- info = pkg_malloc(infolen);
|
|
|
- if (!info) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): out of memory\n");
|
|
|
- pkg_free(command);
|
|
|
- pkg_free(agent);
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- sprintf(info, "from:%.*s,to:%.*s,fromtag:%.*s,totag:%.*s",
|
|
|
- fromAddr.len, fromAddr.s, toAddr.len, toAddr.s,
|
|
|
- fromTag.len, fromTag.s, toTag.len, toTag.s);
|
|
|
- if (isRTPAsymmetric(userAgent)) {
|
|
|
- strcat(info, ",asymmetric");
|
|
|
- }
|
|
|
-
|
|
|
- snprintf(ptr, command + cmdlen - ptr, " %s %.*s %s %.*s %s %s info=%s\n",
|
|
|
- clientIP, fromDomain.len, fromDomain.s, fromType,
|
|
|
- toDomain.len, toDomain.s, toType, agent, info);
|
|
|
-
|
|
|
- pkg_free(info);
|
|
|
- pkg_free(agent);
|
|
|
-
|
|
|
- result = sendMediaproxyCommand(command);
|
|
|
-
|
|
|
- pkg_free(command);
|
|
|
-
|
|
|
- if (result == NULL)
|
|
|
- return -1;
|
|
|
-
|
|
|
- count = getTokens(result, tokens, sizeof(tokens)/sizeof(str));
|
|
|
-
|
|
|
- if (count == 0) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): empty response from mediaproxy\n");
|
|
|
- return -1;
|
|
|
- } else if (count<streamCount+1) {
|
|
|
- if (request) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): insufficient ports returned "
|
|
|
- "from mediaproxy: got %d, expected %d\n", count-1, streamCount);
|
|
|
- return -1;
|
|
|
- } else {
|
|
|
- LOG(L_WARN, "warning: use_media_proxy(): broken client. Called UA "
|
|
|
- "added extra media stream(s) in the OK reply\n");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (sessionIP.s && !isAnyAddress(sessionIP)) {
|
|
|
- success = replaceElement(msg, &sessionIP, &tokens[0]);
|
|
|
- if (!success) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): failed to replace "
|
|
|
- "session-level media IP in SDP body\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- portCount = min(count-1, streamCount);
|
|
|
-
|
|
|
- for (i=0; i<portCount; i++) {
|
|
|
- // check. is this really necessary?
|
|
|
- port = strtoint(&tokens[i+1]);
|
|
|
- if (port <= 0 || port > 65535) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): invalid port returned "
|
|
|
- "by mediaproxy: %.*s\n", tokens[i+1].len, tokens[i+1].s);
|
|
|
- //return -1;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (streams[i].port.len!=1 || streams[i].port.s[0]!='0') {
|
|
|
- success = replaceElement(msg, &(streams[i].port), &tokens[i+1]);
|
|
|
- if (!success) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): failed to replace "
|
|
|
- "port in media stream nr. %d\n", i+1);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (streams[i].localIP && !isAnyAddress(streams[i].ip)) {
|
|
|
- success = replaceElement(msg, &(streams[i].ip), &tokens[0]);
|
|
|
- if (!success) {
|
|
|
- LOG(L_ERR, "error: use_media_proxy(): failed to replace "
|
|
|
- "IP address in media stream nr. %d\n", i+1);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* Module management: initialization/destroy/function-parameter-fixing/... */
|
|
|
-
|
|
|
-static int
|
|
|
-mod_init(void)
|
|
|
-{
|
|
|
- bind_usrloc_t ul_bind_usrloc;
|
|
|
-
|
|
|
- if (natpingInterval > 0) {
|
|
|
- ul_bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
|
|
|
- if (!ul_bind_usrloc) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/mod_init(): can't find the usrloc "
|
|
|
- "module. Check if usrloc.so is loaded.\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- if (ul_bind_usrloc(&userLocation) < 0) {
|
|
|
- LOG(L_ERR, "error: mediaproxy/mod_init(): can't access the usrloc module.\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- register_timer(pingClients, NULL, natpingInterval);
|
|
|
- }
|
|
|
-
|
|
|
- checkAsymmetricFile(&sipAsymmetrics);
|
|
|
- checkAsymmetricFile(&rtpAsymmetrics);
|
|
|
-
|
|
|
- // children won't benefit from this. figure another way
|
|
|
- //register_timer(checkAsymmetricFiles, NULL, 5);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|