diff options
author | Sean Bright <sean@malleable.com> | 2013-01-29 22:58:33 +0000 |
---|---|---|
committer | Sean Bright <sean@malleable.com> | 2013-01-29 22:58:33 +0000 |
commit | 693d609081031d17e57cc2b3ad7408947120dc48 (patch) | |
tree | 8057900da93bb4d8111512cf3d4e17475996ff29 /channels/iax2 | |
parent | ffaf79b1eb7e7a91d2262b5e981ed4fa7142f9a9 (diff) |
Move the ancillary iax2 source files into a separate sub-directory.
This patch just moves the IAX2 source and header files into a separate iax2
sub-directory in the channels directory, similar to how the sip source files are
structured.
The only thing that was added was an #ifndef to protect provision.h from multiple
inclusion.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@380433 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels/iax2')
-rw-r--r-- | channels/iax2/include/iax2.h | 301 | ||||
-rw-r--r-- | channels/iax2/include/parser.h | 177 | ||||
-rw-r--r-- | channels/iax2/include/provision.h | 58 | ||||
-rw-r--r-- | channels/iax2/parser.c | 1294 | ||||
-rw-r--r-- | channels/iax2/provision.c | 566 |
5 files changed, 2396 insertions, 0 deletions
diff --git a/channels/iax2/include/iax2.h b/channels/iax2/include/iax2.h new file mode 100644 index 000000000..ca9ab74dd --- /dev/null +++ b/channels/iax2/include/iax2.h @@ -0,0 +1,301 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Implementation of Inter-Asterisk eXchange + * + * Copyright (C) 2003, Digium + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +/*! \file + * \brief + * + * Implementation of Inter-Asterisk eXchange, version 2 + * \ref iax2-parser.c + * \ref iax2-parser.h + * \ref chan_iax2.c + */ + +#ifndef _IAX2_H +#define _IAX2_H + +/* Max version of IAX protocol we support */ +#define IAX_PROTO_VERSION 2 + +/* NOTE: It is recommended that IAX_MAX_CALLS be a power of 2, but it is not + * required. The maximum number of calls supported by the protocol is 32768. + * + * For LOW_MEMORY, we use 2049 for compatibility with earlier code because + * callno 2048 leaked out when the intended callno range was 2 - 2047. */ +#if defined(LOW_MEMORY) +#define IAX_MAX_CALLS 2049 +#else +#define IAX_MAX_CALLS 32768 +#endif + +#define IAX_FLAG_FULL 0x8000 + +#define IAX_FLAG_RETRANS 0x8000 + +#define IAX_FLAG_SC_LOG 0x80 + +#define IAX_MAX_SHIFT 0x3F + +#define IAX_WINDOW 64 + +/*! Subclass for AST_FRAME_IAX */ +enum iax_frame_subclass { + IAX_COMMAND_NEW = 1, + IAX_COMMAND_PING = 2, + IAX_COMMAND_PONG = 3, + IAX_COMMAND_ACK = 4, + IAX_COMMAND_HANGUP = 5, + IAX_COMMAND_REJECT = 6, + IAX_COMMAND_ACCEPT = 7, + IAX_COMMAND_AUTHREQ = 8, + IAX_COMMAND_AUTHREP = 9, + IAX_COMMAND_INVAL = 10, + IAX_COMMAND_LAGRQ = 11, + IAX_COMMAND_LAGRP = 12, + /*! Registration request */ + IAX_COMMAND_REGREQ = 13, + /*! Registration authentication required */ + IAX_COMMAND_REGAUTH = 14, + /*! Registration accepted */ + IAX_COMMAND_REGACK = 15, + /*! Registration rejected */ + IAX_COMMAND_REGREJ = 16, + /*! Force release of registration */ + IAX_COMMAND_REGREL = 17, + /*! If we receive voice before valid first voice frame, send this */ + IAX_COMMAND_VNAK = 18, + /*! Request status of a dialplan entry */ + IAX_COMMAND_DPREQ = 19, + /*! Request status of a dialplan entry */ + IAX_COMMAND_DPREP = 20, + /*! Request a dial on channel brought up TBD */ + IAX_COMMAND_DIAL = 21, + /*! Transfer Request */ + IAX_COMMAND_TXREQ = 22, + /*! Transfer Connect */ + IAX_COMMAND_TXCNT = 23, + /*! Transfer Accepted */ + IAX_COMMAND_TXACC = 24, + /*! Transfer ready */ + IAX_COMMAND_TXREADY = 25, + /*! Transfer release */ + IAX_COMMAND_TXREL = 26, + /*! Transfer reject */ + IAX_COMMAND_TXREJ = 27, + /*! Stop audio/video transmission */ + IAX_COMMAND_QUELCH = 28, + /*! Resume audio/video transmission */ + IAX_COMMAND_UNQUELCH = 29, + /*! Like ping, but does not require an open connection */ + IAX_COMMAND_POKE = 30, + /*! Paging description */ + IAX_COMMAND_PAGE = 31, + /*! Stand-alone message waiting indicator */ + IAX_COMMAND_MWI = 32, + /*! Unsupported message received */ + IAX_COMMAND_UNSUPPORT = 33, + /*! Request remote transfer */ + IAX_COMMAND_TRANSFER = 34, + /*! Provision device */ + IAX_COMMAND_PROVISION = 35, + /*! Download firmware */ + IAX_COMMAND_FWDOWNL = 36, + /*! Firmware Data */ + IAX_COMMAND_FWDATA = 37, + /*! Transfer media only */ + IAX_COMMAND_TXMEDIA = 38, + /*! Command to rotate key */ + IAX_COMMAND_RTKEY = 39, + /*! Call number token */ + IAX_COMMAND_CALLTOKEN = 40, +}; + +/*! By default require re-registration once per minute */ +#define IAX_DEFAULT_REG_EXPIRE 60 + +/*! How long to wait before closing bridged call */ +#define IAX_LINGER_TIMEOUT 10 + +#define IAX_DEFAULT_PORTNO 4569 + +/*! IAX Information elements */ +#define IAX_IE_CALLED_NUMBER 1 /*!< Number/extension being called - string */ +#define IAX_IE_CALLING_NUMBER 2 /*!< Calling number - string */ +#define IAX_IE_CALLING_ANI 3 /*!< Calling number ANI for billing - string */ +#define IAX_IE_CALLING_NAME 4 /*!< Name of caller - string */ +#define IAX_IE_CALLED_CONTEXT 5 /*!< Context for number - string */ +#define IAX_IE_USERNAME 6 /*!< Username (peer or user) for authentication - string */ +#define IAX_IE_PASSWORD 7 /*!< Password for authentication - string */ +#define IAX_IE_CAPABILITY 8 /*!< Actual codec capability - unsigned int */ +#define IAX_IE_FORMAT 9 /*!< Desired codec format - unsigned int */ +#define IAX_IE_LANGUAGE 10 /*!< Desired language - string */ +#define IAX_IE_VERSION 11 /*!< Protocol version - short */ +#define IAX_IE_ADSICPE 12 /*!< CPE ADSI capability - short */ +#define IAX_IE_DNID 13 /*!< Originally dialed DNID - string */ +#define IAX_IE_AUTHMETHODS 14 /*!< Authentication method(s) - short */ +#define IAX_IE_CHALLENGE 15 /*!< Challenge data for MD5/RSA - string */ +#define IAX_IE_MD5_RESULT 16 /*!< MD5 challenge result - string */ +#define IAX_IE_RSA_RESULT 17 /*!< RSA challenge result - string */ +#define IAX_IE_APPARENT_ADDR 18 /*!< Apparent address of peer - struct sockaddr_in */ +#define IAX_IE_REFRESH 19 /*!< When to refresh registration - short */ +#define IAX_IE_DPSTATUS 20 /*!< Dialplan status - short */ +#define IAX_IE_CALLNO 21 /*!< Call number of peer - short */ +#define IAX_IE_CAUSE 22 /*!< Cause - string */ +#define IAX_IE_IAX_UNKNOWN 23 /*!< Unknown IAX command - byte */ +#define IAX_IE_MSGCOUNT 24 /*!< How many messages waiting - short */ +#define IAX_IE_AUTOANSWER 25 /*!< Request auto-answering -- none */ +#define IAX_IE_MUSICONHOLD 26 /*!< Request musiconhold with QUELCH -- none or string */ +#define IAX_IE_TRANSFERID 27 /*!< Transfer Request Identifier -- int */ +#define IAX_IE_RDNIS 28 /*!< Referring DNIS -- string */ +#define IAX_IE_PROVISIONING 29 /*!< Provisioning info */ +#define IAX_IE_AESPROVISIONING 30 /*!< AES Provisioning info */ +#define IAX_IE_DATETIME 31 /*!< Date/Time */ +#define IAX_IE_DEVICETYPE 32 /*!< Device Type -- string */ +#define IAX_IE_SERVICEIDENT 33 /*!< Service Identifier -- string */ +#define IAX_IE_FIRMWAREVER 34 /*!< Firmware revision -- u16 */ +#define IAX_IE_FWBLOCKDESC 35 /*!< Firmware block description -- u32 */ +#define IAX_IE_FWBLOCKDATA 36 /*!< Firmware block of data -- raw */ +#define IAX_IE_PROVVER 37 /*!< Provisioning Version (u32) */ +#define IAX_IE_CALLINGPRES 38 /*!< Calling presentation (u8) */ +#define IAX_IE_CALLINGTON 39 /*!< Calling type of number (u8) */ +#define IAX_IE_CALLINGTNS 40 /*!< Calling transit network select (u16) */ +#define IAX_IE_SAMPLINGRATE 41 /*!< Supported sampling rates (u16) */ +#define IAX_IE_CAUSECODE 42 /*!< Hangup cause (u8) */ +#define IAX_IE_ENCRYPTION 43 /*!< Encryption format (u16) */ +#define IAX_IE_ENCKEY 44 /*!< Encryption key (raw) */ +#define IAX_IE_CODEC_PREFS 45 /*!< Codec Negotiation */ + +#define IAX_IE_RR_JITTER 46 /*!< Received jitter (as in RFC1889) u32 */ +#define IAX_IE_RR_LOSS 47 /*!< Received loss (high byte loss pct, low 24 bits loss count, as in rfc1889 */ +#define IAX_IE_RR_PKTS 48 /*!< Received frames (total frames received) u32 */ +#define IAX_IE_RR_DELAY 49 /*!< Max playout delay for received frames (in ms) u16 */ +#define IAX_IE_RR_DROPPED 50 /*!< Dropped frames (presumably by jitterbuf) u32 */ +#define IAX_IE_RR_OOO 51 /*!< Frames received Out of Order u32 */ +#define IAX_IE_VARIABLE 52 /*!< Remote variables */ +#define IAX_IE_OSPTOKEN 53 /*!< OSP token */ +#define IAX_IE_CALLTOKEN 54 /*!< Call number security token */ + +#define IAX_IE_CAPABILITY2 55 /*!< Actual codec capability - u8 version + integer array */ +#define IAX_IE_FORMAT2 56 /*!< Desired codec format - u8 version + integer array */ + +#define IAX_MAX_OSPBLOCK_SIZE 254 /*!< Max OSP token block size, 255 bytes - 1 byte OSP token block index */ +#define IAX_MAX_OSPBLOCK_NUM 4 +#define IAX_MAX_OSPTOKEN_SIZE (IAX_MAX_OSPBLOCK_SIZE * IAX_MAX_OSPBLOCK_NUM) +#define IAX_MAX_OSPBUFF_SIZE (IAX_MAX_OSPTOKEN_SIZE + 16) + +#define IAX_AUTH_PLAINTEXT (1 << 0) +#define IAX_AUTH_MD5 (1 << 1) +#define IAX_AUTH_RSA (1 << 2) + +#define IAX_ENCRYPT_AES128 (1 << 0) +#define IAX_ENCRYPT_KEYROTATE (1 << 15) /*!< Keyrotation support */ + +#define IAX_META_TRUNK 1 /*!< Trunk meta-message */ +#define IAX_META_VIDEO 2 /*!< Video frame */ + +#define IAX_META_TRUNK_SUPERMINI 0 /*!< This trunk frame contains classic supermini frames */ +#define IAX_META_TRUNK_MINI 1 /*!< This trunk frame contains trunked mini frames */ + +#define IAX_RATE_8KHZ (1 << 0) /*!< 8khz sampling (default if absent) */ +#define IAX_RATE_11KHZ (1 << 1) /*!< 11.025khz sampling */ +#define IAX_RATE_16KHZ (1 << 2) /*!< 16khz sampling */ +#define IAX_RATE_22KHZ (1 << 3) /*!< 22.05khz sampling */ +#define IAX_RATE_44KHZ (1 << 4) /*!< 44.1khz sampling */ +#define IAX_RATE_48KHZ (1 << 5) /*!< 48khz sampling */ + +#define IAX_DPSTATUS_EXISTS (1 << 0) +#define IAX_DPSTATUS_CANEXIST (1 << 1) +#define IAX_DPSTATUS_NONEXISTENT (1 << 2) +#define IAX_DPSTATUS_IGNOREPAT (1 << 14) +#define IAX_DPSTATUS_MATCHMORE (1 << 15) + +/*! iax2 format bit field for handling codecs the old way */ +typedef int64_t iax2_format; + +/*!\brief iax2 wrapper function for ast_getformatname */ +const char *iax2_getformatname(iax2_format format); + +/*! Full frames are always delivered reliably */ +struct ast_iax2_full_hdr { + unsigned short scallno; /*!< Source call number -- high bit must be 1 */ + unsigned short dcallno; /*!< Destination call number -- high bit is 1 if retransmission */ + unsigned int ts; /*!< 32-bit timestamp in milliseconds (from 1st transmission) */ + unsigned char oseqno; /*!< Packet number (outgoing) */ + unsigned char iseqno; /*!< Packet number (next incoming expected) */ + unsigned char type; /*!< Frame type */ + unsigned char csub; /*!< Compressed subclass */ + unsigned char iedata[0]; +} __attribute__ ((__packed__)); + +/*! Full frames are always delivered reliably */ +struct ast_iax2_full_enc_hdr { + unsigned short scallno; /*!< Source call number -- high bit must be 1 */ + unsigned short dcallno; /*!< Destination call number -- high bit is 1 if retransmission */ + unsigned char encdata[0]; +} __attribute__ ((__packed__)); + +/*! Mini header is used only for voice frames -- delivered unreliably */ +struct ast_iax2_mini_hdr { + unsigned short callno; /*!< Source call number -- high bit must be 0, rest must be non-zero */ + unsigned short ts; /*!< 16-bit Timestamp (high 16 bits from last ast_iax2_full_hdr) */ + /* Frametype implicitly VOICE_FRAME */ + /* subclass implicit from last ast_iax2_full_hdr */ + unsigned char data[0]; +} __attribute__ ((__packed__)); + +/*! Mini header is used only for voice frames -- delivered unreliably */ +struct ast_iax2_mini_enc_hdr { + unsigned short callno; /*!< Source call number -- high bit must be 0, rest must be non-zero */ + unsigned char encdata[0]; +} __attribute__ ((__packed__)); + +struct ast_iax2_meta_hdr { + unsigned short zeros; /*!< Zeros field -- must be zero */ + unsigned char metacmd; /*!< Meta command */ + unsigned char cmddata; /*!< Command Data */ + unsigned char data[0]; +} __attribute__ ((__packed__)); + +struct ast_iax2_video_hdr { + unsigned short zeros; /*!< Zeros field -- must be zero */ + unsigned short callno; /*!< Video call number */ + unsigned short ts; /*!< Timestamp and mark if present */ + unsigned char data[0]; +} __attribute__ ((__packed__)); + +struct ast_iax2_meta_trunk_hdr { + unsigned int ts; /*!< 32-bit timestamp for all messages */ + unsigned char data[0]; +} __attribute__ ((__packed__)); + +struct ast_iax2_meta_trunk_entry { + unsigned short callno; /*!< Call number */ + unsigned short len; /*!< Length of data for this callno */ +} __attribute__ ((__packed__)); + +/*! When trunktimestamps are used, we use this format instead */ +struct ast_iax2_meta_trunk_mini { + unsigned short len; + struct ast_iax2_mini_hdr mini; /*!< this is an actual miniframe */ +} __attribute__ ((__packed__)); + +#define IAX_FIRMWARE_MAGIC 0x69617879 + +struct ast_iax2_firmware_header { + unsigned int magic; /*!< Magic number */ + unsigned short version; /*!< Software version */ + unsigned char devname[16]; /*!< Device */ + unsigned int datalen; /*!< Data length of file beyond header */ + unsigned char chksum[16]; /*!< Checksum of all data */ + unsigned char data[0]; +} __attribute__ ((__packed__)); +#endif diff --git a/channels/iax2/include/parser.h b/channels/iax2/include/parser.h new file mode 100644 index 000000000..caa121039 --- /dev/null +++ b/channels/iax2/include/parser.h @@ -0,0 +1,177 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Implementation of Inter-Asterisk eXchange + * + * Copyright (C) 2003, Digium + * + * Mark Spencer <markster@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +/*!\file + * \brief Implementation of the IAX2 protocol + */ + +#ifndef _IAX2_PARSER_H +#define _IAX2_PARSER_H + +#include "asterisk/linkedlists.h" +#include "asterisk/crypto.h" +#include "iax2.h" + +struct iax_ies { + char *called_number; + char *calling_number; + char *calling_ani; + char *calling_name; + int calling_ton; + int calling_tns; + int calling_pres; + char *called_context; + char *username; + char *password; + iax2_format capability; + iax2_format format; + char *codec_prefs; + char *language; + int version; + unsigned short adsicpe; + char *dnid; + char *rdnis; + unsigned int authmethods; + unsigned int encmethods; + char *challenge; + char *md5_result; + char *rsa_result; + struct sockaddr_in *apparent_addr; + unsigned short refresh; + unsigned short dpstatus; + unsigned short callno; + char *cause; + unsigned char causecode; + unsigned char iax_unknown; + int msgcount; + int autoanswer; + int musiconhold; + unsigned int transferid; + unsigned int datetime; + char *devicetype; + char *serviceident; + int firmwarever; + unsigned int fwdesc; + unsigned char *fwdata; + unsigned char fwdatalen; + unsigned char *enckey; + unsigned char enckeylen; + unsigned int provver; + unsigned short samprate; + int provverpres; + unsigned int rr_jitter; + unsigned int rr_loss; + unsigned int rr_pkts; + unsigned short rr_delay; + unsigned int rr_dropped; + unsigned int rr_ooo; + struct ast_variable *vars; + char *osptokenblock[IAX_MAX_OSPBLOCK_NUM]; + unsigned int ospblocklength[IAX_MAX_OSPBLOCK_NUM]; + unsigned char calltoken; + unsigned char *calltokendata; +}; + +#define DIRECTION_INGRESS 1 +#define DIRECTION_OUTGRESS 2 + +struct iax_frame { +#ifdef LIBIAX + struct iax_session *session; + struct iax_event *event; +#else + int sockfd; +#endif + + /*! /Our/ call number */ + unsigned short callno; + /*! /Their/ call number */ + unsigned short dcallno; + /*! Start of raw frame (outgoing only) */ + void *data; + /*! Length of frame (outgoing only) */ + int datalen; + /*! How many retries so far? */ + int retries; + /*! Outgoing relative timestamp (ms) */ + unsigned int ts; + /*! How long to wait before retrying */ + int retrytime; + /*! Are we received out of order? */ + unsigned int outoforder:1; + /*! Have we been sent at all yet? */ + unsigned int sentyet:1; + /*! Non-zero if should be sent to transfer peer */ + unsigned int transfer:1; + /*! Non-zero if this is the final message */ + unsigned int final:1; + /*! Ingress or outgres */ + unsigned int direction:2; + /*! Can this frame be cached? */ + unsigned int cacheable:1; + /*! Outgoing Packet sequence number */ + int oseqno; + /*! Next expected incoming packet sequence number */ + int iseqno; + /*! Retransmission ID */ + int retrans; + /*! is this packet encrypted or not. if set this varible holds encryption methods*/ + int encmethods; + /*! store encrypt key */ + ast_aes_encrypt_key ecx; + /*! store decrypt key which corresponds to ecx */ + ast_aes_decrypt_key mydcx; + /*! random data for encryption pad */ + unsigned char semirand[32]; + /*! Easy linking */ + AST_LIST_ENTRY(iax_frame) list; + /*! Actual, isolated frame header */ + struct ast_frame af; + /*! Amount of space _allocated_ for data */ + size_t afdatalen; + unsigned char unused[AST_FRIENDLY_OFFSET]; + unsigned char afdata[0]; /* Data for frame */ +}; + +struct iax_ie_data { + unsigned char buf[1024]; + int pos; +}; + +/* Choose a different function for output */ +void iax_set_output(void (*output)(const char *data)); +/* Choose a different function for errors */ +void iax_set_error(void (*output)(const char *data)); +void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen); +void iax_frame_subclass2str(enum iax_frame_subclass subclass, char *str, size_t len); + +const char *iax_ie2str(int ie); + +int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen); +int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin); +int iax_ie_append_versioned_uint64(struct iax_ie_data *ied, unsigned char ie, unsigned char version, uint64_t value); +int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value); +int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value); +int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str); +int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat); +int iax_ie_append(struct iax_ie_data *ied, unsigned char ie); +int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen); + +int iax_get_frames(void); +int iax_get_iframes(void); +int iax_get_oframes(void); + +void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f); +struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable); +void iax_frame_free(struct iax_frame *fr); +#endif diff --git a/channels/iax2/include/provision.h b/channels/iax2/include/provision.h new file mode 100644 index 000000000..fd5a829c3 --- /dev/null +++ b/channels/iax2/include/provision.h @@ -0,0 +1,58 @@ +/* + * IAX Provisioning Protocol + * + * Sub-information elements + * + * Copyright (C) 2003, Digium + * + * Mark Spencer <markster@digium.com> + * + */ + +/*! \file + * \brief IAX2 Provisioning protocol + */ + +#ifndef __IAX2_PROVISION_H +#define __IAX2_PROVISION_H + +#include "parser.h" + +#define PROV_IE_USEDHCP 1 /* Presense only */ +#define PROV_IE_IPADDR 2 /* 32-bit */ +#define PROV_IE_SUBNET 3 /* 32-bit */ +#define PROV_IE_GATEWAY 4 /* 32-bit */ +#define PROV_IE_PORTNO 5 /* 16-bit */ +#define PROV_IE_USER 6 /* < 20 bytes */ +#define PROV_IE_PASS 7 /* < 20 bytes */ +#define PROV_IE_SERVERUSER 8 /* < 20 bytes */ +#define PROV_IE_SERVERPASS 9 /* < 20 bytes */ +#define PROV_IE_LANG 10 /* < 10 bytes */ +#define PROV_IE_TOS 11 /* 8-bits */ +#define PROV_IE_FLAGS 12 /* 32-bits */ +#define PROV_IE_FORMAT 13 /* 32-bits */ +#define PROV_IE_AESKEY 14 /* 128-bits */ +#define PROV_IE_SERVERIP 15 /* 32-bits */ +#define PROV_IE_SERVERPORT 16 /* 16-bits */ +#define PROV_IE_NEWAESKEY 17 /* 128-bits */ +#define PROV_IE_PROVVER 18 /* 32-bits */ +#define PROV_IE_ALTSERVER 19 /* 32-bits */ + +#define PROV_FLAG_REGISTER (1 << 0) +#define PROV_FLAG_SECURE (1 << 1) +#define PROV_FLAG_HEARTBEAT (1 << 2) +#define PROV_FLAG_DEBUG (1 << 3) + +#define PROV_FLAG_DIS_CALLERID (1 << 4) /* Caller-ID Disabled */ +#define PROV_FLAG_DIS_CALLWAIT (1 << 5) /* Caller-ID / Call Waiting Disable */ +#define PROV_FLAG_DIS_CIDCW (1 << 6) /* CID/CW Disabled */ +#define PROV_FLAG_DIS_THREEWAY (1 << 7) /* Three-way calling, transfer disabled */ + +char *iax_provflags2str(char *buf, int buflen, unsigned int flags); +int iax_provision_reload(int reload); +int iax_provision_unload(void); +int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force); +int iax_provision_version(unsigned int *signature, const char *template, int force); +char *iax_prov_complete_template(const char *line, const char *word, int pos, int state); + +#endif diff --git a/channels/iax2/parser.c b/channels/iax2/parser.c new file mode 100644 index 000000000..878d70186 --- /dev/null +++ b/channels/iax2/parser.c @@ -0,0 +1,1294 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Implementation of Inter-Asterisk eXchange Protocol, v 2 + * + * \author Mark Spencer <markster@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "asterisk/frame.h" +#include "asterisk/utils.h" +#include "asterisk/unaligned.h" +#include "asterisk/config.h" +#include "asterisk/lock.h" +#include "asterisk/threadstorage.h" + +#include "include/iax2.h" +#include "include/parser.h" +#include "include/provision.h" + +static int frames = 0; +static int iframes = 0; +static int oframes = 0; + +#if !defined(LOW_MEMORY) +static void frame_cache_cleanup(void *data); + +/*! \brief A per-thread cache of iax_frame structures */ +AST_THREADSTORAGE_CUSTOM(frame_cache, NULL, frame_cache_cleanup); + +/*! \brief This is just so iax_frames, a list head struct for holding a list of + * iax_frame structures, is defined. */ +AST_LIST_HEAD_NOLOCK(iax_frame_list, iax_frame); + +struct iax_frames { + struct iax_frame_list list; + size_t size; +}; + +#define FRAME_CACHE_MAX_SIZE 20 +#endif + +static void internaloutput(const char *str) +{ + fputs(str, stdout); +} + +static void internalerror(const char *str) +{ + fprintf(stderr, "WARNING: %s", str); +} + +static void (*outputf)(const char *str) = internaloutput; +static void (*errorf)(const char *str) = internalerror; + +static void dump_addr(char *output, int maxlen, void *value, int len) +{ + struct sockaddr_in sin; + if (len == (int)sizeof(sin)) { + memcpy(&sin, value, len); + snprintf(output, maxlen, "IPV4 %s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } else { + ast_copy_string(output, "Invalid Address", maxlen); + } +} + +static void dump_string_hex(char *output, int maxlen, void *value, int len) +{ + int i = 0; + + while (len-- && (i + 1) * 4 < maxlen) { + sprintf(output + (4 * i), "\\x%2.2x", *((unsigned char *)value + i)); + i++; + } +} + +static void dump_string(char *output, int maxlen, void *value, int len) +{ + maxlen--; + if (maxlen > len) + maxlen = len; + strncpy(output, value, maxlen); + output[maxlen] = '\0'; +} + +static void dump_prefs(char *output, int maxlen, void *value, int len) +{ + struct ast_codec_pref pref; + int total_len = 0; + + maxlen--; + total_len = maxlen; + + if (maxlen > len) + maxlen = len; + + strncpy(output, value, maxlen); + output[maxlen] = '\0'; + + ast_codec_pref_convert(&pref, output, total_len, 0); + memset(output,0,total_len); + ast_codec_pref_string(&pref, output, total_len); +} + +static void dump_int(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned int)) + snprintf(output, maxlen, "%lu", (unsigned long)ntohl(get_unaligned_uint32(value))); + else + ast_copy_string(output, "Invalid INT", maxlen); +} + +static void dump_short(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned short)) + snprintf(output, maxlen, "%d", ntohs(get_unaligned_uint16(value))); + else + ast_copy_string(output, "Invalid SHORT", maxlen); +} + +static void dump_byte(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned char)) + snprintf(output, maxlen, "%d", *((unsigned char *)value)); + else + ast_copy_string(output, "Invalid BYTE", maxlen); +} + +static void dump_datetime(char *output, int maxlen, void *value, int len) +{ + struct ast_tm tm; + unsigned long val = (unsigned long) ntohl(get_unaligned_uint32(value)); + if (len == (int)sizeof(unsigned int)) { + tm.tm_sec = (val & 0x1f) << 1; + tm.tm_min = (val >> 5) & 0x3f; + tm.tm_hour = (val >> 11) & 0x1f; + tm.tm_mday = (val >> 16) & 0x1f; + tm.tm_mon = ((val >> 21) & 0x0f) - 1; + tm.tm_year = ((val >> 25) & 0x7f) + 100; + ast_strftime(output, maxlen, "%Y-%m-%d %T", &tm); + } else + ast_copy_string(output, "Invalid DATETIME format!", maxlen); +} + +static void dump_ipaddr(char *output, int maxlen, void *value, int len) +{ + struct sockaddr_in sin; + if (len == (int)sizeof(unsigned int)) { + memcpy(&sin.sin_addr, value, len); + snprintf(output, maxlen, "%s", ast_inet_ntoa(sin.sin_addr)); + } else + ast_copy_string(output, "Invalid IPADDR", maxlen); +} + + +static void dump_prov_flags(char *output, int maxlen, void *value, int len) +{ + char buf[256] = ""; + if (len == (int)sizeof(unsigned int)) + snprintf(output, maxlen, "%lu (%s)", (unsigned long)ntohl(get_unaligned_uint32(value)), + iax_provflags2str(buf, sizeof(buf), ntohl(get_unaligned_uint32(value)))); + else + ast_copy_string(output, "Invalid INT", maxlen); +} + +static void dump_samprate(char *output, int maxlen, void *value, int len) +{ + char tmp[256]=""; + int sr; + if (len == (int)sizeof(unsigned short)) { + sr = ntohs(*((unsigned short *)value)); + if (sr & IAX_RATE_8KHZ) + strcat(tmp, ",8khz"); + if (sr & IAX_RATE_11KHZ) + strcat(tmp, ",11.025khz"); + if (sr & IAX_RATE_16KHZ) + strcat(tmp, ",16khz"); + if (sr & IAX_RATE_22KHZ) + strcat(tmp, ",22.05khz"); + if (sr & IAX_RATE_44KHZ) + strcat(tmp, ",44.1khz"); + if (sr & IAX_RATE_48KHZ) + strcat(tmp, ",48khz"); + if (strlen(tmp)) + ast_copy_string(output, &tmp[1], maxlen); + else + ast_copy_string(output, "None Specified!\n", maxlen); + } else + ast_copy_string(output, "Invalid SHORT", maxlen); + +} + +static void dump_versioned_codec(char *output, int maxlen, void *value, int len) +{ + char *version = (char *) value; + if (version[0] == 0) { + if (len == (int) (sizeof(iax2_format) + sizeof(char))) { + iax2_format codec = ntohll(get_unaligned_uint64(value + 1)); + ast_copy_string(output, iax2_getformatname(codec), maxlen); + } else { + ast_copy_string(output, "Invalid length!", maxlen); + } + } else { + ast_copy_string(output, "Unknown version!", maxlen); + } +} + +static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len); +static void dump_prov(char *output, int maxlen, void *value, int len) +{ + dump_prov_ies(output, maxlen, value, len); +} + +static struct iax2_ie { + int ie; + char *name; + void (*dump)(char *output, int maxlen, void *value, int len); +} infoelts[] = { + { IAX_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string }, + { IAX_IE_CALLING_NUMBER, "CALLING NUMBER", dump_string }, + { IAX_IE_CALLING_ANI, "ANI", dump_string }, + { IAX_IE_CALLING_NAME, "CALLING NAME", dump_string }, + { IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string }, + { IAX_IE_USERNAME, "USERNAME", dump_string }, + { IAX_IE_PASSWORD, "PASSWORD", dump_string }, + { IAX_IE_CAPABILITY, "CAPABILITY", dump_int }, + { IAX_IE_CAPABILITY2, "CAPABILITY2", dump_versioned_codec }, + { IAX_IE_FORMAT, "FORMAT", dump_int }, + { IAX_IE_FORMAT2, "FORMAT2", dump_versioned_codec }, + { IAX_IE_LANGUAGE, "LANGUAGE", dump_string }, + { IAX_IE_VERSION, "VERSION", dump_short }, + { IAX_IE_ADSICPE, "ADSICPE", dump_short }, + { IAX_IE_DNID, "DNID", dump_string }, + { IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short }, + { IAX_IE_CHALLENGE, "CHALLENGE", dump_string_hex }, + { IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string }, + { IAX_IE_RSA_RESULT, "RSA RESULT", dump_string }, + { IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr }, + { IAX_IE_REFRESH, "REFRESH", dump_short }, + { IAX_IE_DPSTATUS, "DIALPLAN STATUS", dump_short }, + { IAX_IE_CALLNO, "CALL NUMBER", dump_short }, + { IAX_IE_CAUSE, "CAUSE", dump_string }, + { IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte }, + { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short }, + { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" }, + { IAX_IE_TRANSFERID, "TRANSFER ID", dump_int }, + { IAX_IE_RDNIS, "REFERRING DNIS", dump_string }, + { IAX_IE_PROVISIONING, "PROVISIONING", dump_prov }, + { IAX_IE_AESPROVISIONING, "AES PROVISIONG" }, + { IAX_IE_DATETIME, "DATE TIME", dump_datetime }, + { IAX_IE_DEVICETYPE, "DEVICE TYPE", dump_string }, + { IAX_IE_SERVICEIDENT, "SERVICE IDENT", dump_string }, + { IAX_IE_FIRMWAREVER, "FIRMWARE VER", dump_short }, + { IAX_IE_FWBLOCKDESC, "FW BLOCK DESC", dump_int }, + { IAX_IE_FWBLOCKDATA, "FW BLOCK DATA" }, + { IAX_IE_PROVVER, "PROVISIONG VER", dump_int }, + { IAX_IE_CALLINGPRES, "CALLING PRESNTN", dump_byte }, + { IAX_IE_CALLINGTON, "CALLING TYPEOFNUM", dump_byte }, + { IAX_IE_CALLINGTNS, "CALLING TRANSITNET", dump_short }, + { IAX_IE_SAMPLINGRATE, "SAMPLINGRATE", dump_samprate }, + { IAX_IE_CAUSECODE, "CAUSE CODE", dump_byte }, + { IAX_IE_ENCRYPTION, "ENCRYPTION", dump_short }, + { IAX_IE_ENCKEY, "ENCRYPTION KEY" }, + { IAX_IE_CODEC_PREFS, "CODEC_PREFS", dump_prefs }, + { IAX_IE_RR_JITTER, "RR_JITTER", dump_int }, + { IAX_IE_RR_LOSS, "RR_LOSS", dump_int }, + { IAX_IE_RR_PKTS, "RR_PKTS", dump_int }, + { IAX_IE_RR_DELAY, "RR_DELAY", dump_short }, + { IAX_IE_RR_DROPPED, "RR_DROPPED", dump_int }, + { IAX_IE_RR_OOO, "RR_OUTOFORDER", dump_int }, + { IAX_IE_VARIABLE, "VARIABLE", dump_string }, + { IAX_IE_OSPTOKEN, "OSPTOKEN" }, + { IAX_IE_CALLTOKEN, "CALLTOKEN" }, +}; + +static const struct iax2_ie prov_ies[] = { + { PROV_IE_USEDHCP, "USEDHCP" }, + { PROV_IE_IPADDR, "IPADDR", dump_ipaddr }, + { PROV_IE_SUBNET, "SUBNET", dump_ipaddr }, + { PROV_IE_GATEWAY, "GATEWAY", dump_ipaddr }, + { PROV_IE_PORTNO, "BINDPORT", dump_short }, + { PROV_IE_USER, "USERNAME", dump_string }, + { PROV_IE_PASS, "PASSWORD", dump_string }, + { PROV_IE_LANG, "LANGUAGE", dump_string }, + { PROV_IE_TOS, "TYPEOFSERVICE", dump_byte }, + { PROV_IE_FLAGS, "FLAGS", dump_prov_flags }, + { PROV_IE_FORMAT, "FORMAT", dump_int }, + { PROV_IE_AESKEY, "AESKEY" }, + { PROV_IE_SERVERIP, "SERVERIP", dump_ipaddr }, + { PROV_IE_SERVERPORT, "SERVERPORT", dump_short }, + { PROV_IE_NEWAESKEY, "NEWAESKEY" }, + { PROV_IE_PROVVER, "PROV VERSION", dump_int }, + { PROV_IE_ALTSERVER, "ALTSERVERIP", dump_ipaddr }, +}; + +const char *iax_ie2str(int ie) +{ + int x; + for (x = 0; x < ARRAY_LEN(infoelts); x++) { + if (infoelts[x].ie == ie) + return infoelts[x].name; + } + return "Unknown IE"; +} + + +static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len) +{ + int ielen; + int ie; + int x; + int found; + char interp[80]; + char tmp[256]; + if (len < 2) + return; + strcpy(output, "\n"); + maxlen -= strlen(output); output += strlen(output); + while(len > 2) { + ie = iedata[0]; + ielen = iedata[1]; + if (ielen + 2> len) { + snprintf(tmp, (int)sizeof(tmp), "Total Prov IE length of %d bytes exceeds remaining prov frame length of %d bytes\n", ielen + 2, len); + ast_copy_string(output, tmp, maxlen); + maxlen -= strlen(output); + output += strlen(output); + return; + } + found = 0; + for (x=0;x<(int)sizeof(prov_ies) / (int)sizeof(prov_ies[0]); x++) { + if (prov_ies[x].ie == ie) { + if (prov_ies[x].dump) { + prov_ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen); + snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", prov_ies[x].name, interp); + ast_copy_string(output, tmp, maxlen); + maxlen -= strlen(output); output += strlen(output); + } else { + if (ielen) + snprintf(interp, (int)sizeof(interp), "%d bytes", ielen); + else + strcpy(interp, "Present"); + snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", prov_ies[x].name, interp); + ast_copy_string(output, tmp, maxlen); + maxlen -= strlen(output); output += strlen(output); + } + found++; + } + } + if (!found) { + snprintf(tmp, (int)sizeof(tmp), " Unknown Prov IE %03d : Present\n", ie); + ast_copy_string(output, tmp, maxlen); + maxlen -= strlen(output); output += strlen(output); + } + iedata += (2 + ielen); + len -= (2 + ielen); + } +} + +static void dump_ies(unsigned char *iedata, int len) +{ + int ielen; + int ie; + int x; + int found; + char interp[1024]; + char tmp[1024]; + if (len < 2) + return; + while(len > 2) { + ie = iedata[0]; + ielen = iedata[1]; + if (ielen + 2> len) { + snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len); + outputf(tmp); + return; + } + found = 0; + for (x = 0; x < ARRAY_LEN(infoelts); x++) { + if (infoelts[x].ie == ie) { + if (infoelts[x].dump) { + infoelts[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen); + snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", infoelts[x].name, interp); + outputf(tmp); + } else { + if (ielen) + snprintf(interp, (int)sizeof(interp), "%d bytes", ielen); + else + strcpy(interp, "Present"); + snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", infoelts[x].name, interp); + outputf(tmp); + } + found++; + } + } + if (!found) { + snprintf(tmp, (int)sizeof(tmp), " Unknown IE %03d : Present\n", ie); + outputf(tmp); + } + iedata += (2 + ielen); + len -= (2 + ielen); + } + outputf("\n"); +} + +void iax_frame_subclass2str(enum iax_frame_subclass subclass, char *str, size_t len) +{ + const char *cmd = "Unknown"; + + /* if an error occurs here during compile, that means a new iax frame subclass + * has been added to the iax_frame_subclass enum. Add the new subclass to the + * switch case and make sure to update it with a new string representation. */ + switch (subclass) { + case IAX_COMMAND_NEW: + cmd = "NEW "; + break; + case IAX_COMMAND_PING: + cmd = "PING "; + break; + case IAX_COMMAND_PONG: + cmd = "PONG "; + break; + case IAX_COMMAND_ACK: + cmd = "ACK "; + break; + case IAX_COMMAND_HANGUP: + cmd = "HANGUP "; + break; + case IAX_COMMAND_REJECT: + cmd = "REJECT "; + break; + case IAX_COMMAND_ACCEPT: + cmd = "ACCEPT "; + break; + case IAX_COMMAND_AUTHREQ: + cmd = "AUTHREQ"; + break; + case IAX_COMMAND_AUTHREP: + cmd = "AUTHREP"; + break; + case IAX_COMMAND_INVAL: + cmd = "INVAL "; + break; + case IAX_COMMAND_LAGRQ: + cmd = "LAGRQ "; + break; + case IAX_COMMAND_LAGRP: + cmd = "LAGRP "; + break; + case IAX_COMMAND_REGREQ: + cmd = "REGREQ "; + break; + case IAX_COMMAND_REGAUTH: + cmd = "REGAUTH"; + break; + case IAX_COMMAND_REGACK: + cmd = "REGACK "; + break; + case IAX_COMMAND_REGREJ: + cmd = "REGREJ "; + break; + case IAX_COMMAND_REGREL: + cmd = "REGREL "; + break; + case IAX_COMMAND_VNAK: + cmd = "VNAK "; + break; + case IAX_COMMAND_DPREQ: + cmd = "DPREQ "; + break; + case IAX_COMMAND_DPREP: + cmd = "DPREP "; + break; + case IAX_COMMAND_DIAL: + cmd = "DIAL "; + break; + case IAX_COMMAND_TXREQ: + cmd = "TXREQ "; + break; + case IAX_COMMAND_TXCNT: + cmd = "TXCNT "; + break; + case IAX_COMMAND_TXACC: + cmd = "TXACC "; + break; + case IAX_COMMAND_TXREADY: + cmd = "TXREADY"; + break; + case IAX_COMMAND_TXREL: + cmd = "TXREL "; + break; + case IAX_COMMAND_TXREJ: + cmd = "TXREJ "; + break; + case IAX_COMMAND_QUELCH: + cmd = "QUELCH "; + break; + case IAX_COMMAND_UNQUELCH: + cmd = "UNQULCH"; + break; + case IAX_COMMAND_POKE: + cmd = "POKE "; + break; + case IAX_COMMAND_PAGE: + cmd = "PAGE "; + break; + case IAX_COMMAND_MWI: + cmd = "MWI "; + break; + case IAX_COMMAND_UNSUPPORT: + cmd = "UNSPRTD"; + break; + case IAX_COMMAND_TRANSFER: + cmd = "TRANSFR"; + break; + case IAX_COMMAND_PROVISION: + cmd = "PROVISN"; + break; + case IAX_COMMAND_FWDOWNL: + cmd = "FWDWNLD"; + break; + case IAX_COMMAND_FWDATA: + cmd = "FWDATA "; + break; + case IAX_COMMAND_TXMEDIA: + cmd = "TXMEDIA"; + break; + case IAX_COMMAND_RTKEY: + cmd = "RTKEY "; + break; + case IAX_COMMAND_CALLTOKEN: + cmd = "CTOKEN "; + break; + } + ast_copy_string(str, cmd, len); +} + +void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen) +{ + const char *framelist[] = { + "(0?)", + "DTMF_E ", + "VOICE ", + "VIDEO ", + "CONTROL", + "NULL ", + "IAX ", + "TEXT ", + "IMAGE ", + "HTML ", + "CNG ", + "MODEM ", + "DTMF_B ", + }; + const char *cmds[] = { + "(0?)", + "HANGUP ", + "RING ", + "RINGING", + "ANSWER ", + "BUSY ", + "TKOFFHK", + "OFFHOOK", + "CONGSTN", + "FLASH ", + "WINK ", + "OPTION ", + "RDKEY ", + "RDUNKEY", + "PROGRES", + "PROCDNG", + "HOLD ", + "UNHOLD ", + "VIDUPDT", + "T38 ", + "SRCUPDT", + "TXFER ", + "CNLINE ", + "REDIR ", + }; + struct ast_iax2_full_hdr *fh; + char retries[20]; + char class2[20]; + char subclass2[20]; + const char *class; + const char *subclass; + char *dir; + char tmp[512]; + + switch(rx) { + case 0: + dir = "Tx"; + break; + case 2: + dir = "TE"; + break; + case 3: + dir = "RD"; + break; + default: + dir = "Rx"; + break; + } + if (f) { + fh = f->data; + snprintf(retries, sizeof(retries), "%03d", f->retries); + } else { + fh = fhi; + if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) + strcpy(retries, "Yes"); + else + strcpy(retries, " No"); + } + if (!(ntohs(fh->scallno) & IAX_FLAG_FULL)) { + /* Don't mess with mini-frames */ + return; + } + if (fh->type >= ARRAY_LEN(framelist)) { + snprintf(class2, sizeof(class2), "(%d?)", fh->type); + class = class2; + } else { + class = framelist[(int)fh->type]; + } + if (fh->type == AST_FRAME_DTMF_BEGIN || fh->type == AST_FRAME_DTMF_END) { + sprintf(subclass2, "%c", fh->csub); + subclass = subclass2; + } else if (fh->type == AST_FRAME_IAX) { + iax_frame_subclass2str((int)fh->csub, subclass2, sizeof(subclass2)); + subclass = subclass2; + } else if (fh->type == AST_FRAME_CONTROL) { + if (fh->csub >= ARRAY_LEN(cmds)) { + snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub); + subclass = subclass2; + } else { + subclass = cmds[(int)fh->csub]; + } + } else { + snprintf(subclass2, sizeof(subclass2), "%d", fh->csub); + subclass = subclass2; + } + snprintf(tmp, sizeof(tmp), + "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s\n", + dir, + retries, fh->oseqno, fh->iseqno, class, subclass); + outputf(tmp); + snprintf(tmp, sizeof(tmp), + " Timestamp: %05lums SCall: %5.5d DCall: %5.5d [%s:%d]\n", + (unsigned long)ntohl(fh->ts), + ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, + ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + outputf(tmp); + if (fh->type == AST_FRAME_IAX) + dump_ies(fh->iedata, datalen); +} + +int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen) +{ + char tmp[256]; + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", iax_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); + errorf(tmp); + return -1; + } + ied->buf[ied->pos++] = ie; + ied->buf[ied->pos++] = datalen; + memcpy(ied->buf + ied->pos, data, datalen); + ied->pos += datalen; + return 0; +} + +int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin) +{ + return iax_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in)); +} + +int iax_ie_append_versioned_uint64(struct iax_ie_data *ied, unsigned char ie, unsigned char version, uint64_t value) +{ + struct _local { + unsigned char version; + uint64_t value; + } __attribute__((packed)) newval = { version, }; + put_unaligned_uint64(&newval.value, htonll(value)); + return iax_ie_append_raw(ied, ie, &newval, (int) sizeof(newval)); +} + +int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value) +{ + unsigned int newval; + newval = htonl(value); + return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); +} + +int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value) +{ + unsigned short newval; + newval = htons(value); + return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); +} + +int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str) +{ + return iax_ie_append_raw(ied, ie, str, strlen(str)); +} + +int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat) +{ + return iax_ie_append_raw(ied, ie, &dat, 1); +} + +int iax_ie_append(struct iax_ie_data *ied, unsigned char ie) +{ + return iax_ie_append_raw(ied, ie, NULL, 0); +} + +void iax_set_output(void (*func)(const char *)) +{ + outputf = func; +} + +void iax_set_error(void (*func)(const char *)) +{ + errorf = func; +} + +int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen) +{ + /* Parse data into information elements */ + int len; + int ie; + char tmp[256], *tmp2; + struct ast_variable *var, *var2, *prev; + unsigned int count; + memset(ies, 0, (int)sizeof(struct iax_ies)); + ies->msgcount = -1; + ies->firmwarever = -1; + ies->calling_ton = -1; + ies->calling_tns = -1; + ies->calling_pres = -1; + ies->samprate = IAX_RATE_8KHZ; + while(datalen >= 2) { + ie = data[0]; + len = data[1]; + if (len > datalen - 2) { + errorf("Information element length exceeds message size\n"); + return -1; + } + switch(ie) { + case IAX_IE_CALLED_NUMBER: + ies->called_number = (char *)data + 2; + break; + case IAX_IE_CALLING_NUMBER: + ies->calling_number = (char *)data + 2; + break; + case IAX_IE_CALLING_ANI: + ies->calling_ani = (char *)data + 2; + break; + case IAX_IE_CALLING_NAME: + ies->calling_name = (char *)data + 2; + break; + case IAX_IE_CALLED_CONTEXT: + ies->called_context = (char *)data + 2; + break; + case IAX_IE_USERNAME: + ies->username = (char *)data + 2; + break; + case IAX_IE_PASSWORD: + ies->password = (char *)data + 2; + break; + case IAX_IE_CODEC_PREFS: + ies->codec_prefs = (char *)data + 2; + break; + case IAX_IE_CAPABILITY: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else if (ies->capability == 0) { /* Don't overwrite capability2, if specified */ + ies->capability = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_CAPABILITY2: + { + int version = data[2]; + if (version == 0) { + if (len != (int)sizeof(char) + sizeof(iax2_format)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", (int) (sizeof(iax2_format) + sizeof(char)), len); + errorf(tmp); + } else { + ies->capability = (iax2_format) ntohll(get_unaligned_uint64(data + 3)); + } + } /* else unknown version */ + } + break; + case IAX_IE_FORMAT: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else if (ies->format == 0) { /* Don't overwrite format2, if specified */ + ies->format = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_FORMAT2: + { + int version = data[2]; + if (version == 0) { + if (len != (int)sizeof(char) + sizeof(iax2_format)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", (int) (sizeof(iax2_format) + sizeof(char)), len); + errorf(tmp); + } else { + ies->format = (iax2_format) ntohll(get_unaligned_uint64(data + 3)); + } + } /* else unknown version */ + } + break; + case IAX_IE_LANGUAGE: + ies->language = (char *)data + 2; + break; + case IAX_IE_VERSION: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->version = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_ADSICPE: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting adsicpe to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->adsicpe = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_SAMPLINGRATE: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting samplingrate to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->samprate = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_DNID: + ies->dnid = (char *)data + 2; + break; + case IAX_IE_RDNIS: + ies->rdnis = (char *)data + 2; + break; + case IAX_IE_AUTHMETHODS: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->authmethods = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_ENCRYPTION: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting encryption to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->encmethods = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_CHALLENGE: + ies->challenge = (char *)data + 2; + break; + case IAX_IE_MD5_RESULT: + ies->md5_result = (char *)data + 2; + break; + case IAX_IE_RSA_RESULT: + ies->rsa_result = (char *)data + 2; + break; + case IAX_IE_APPARENT_ADDR: + ies->apparent_addr = ((struct sockaddr_in *)(data + 2)); + break; + case IAX_IE_REFRESH: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting refresh to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->refresh = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_DPSTATUS: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting dpstatus to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->dpstatus = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_CALLNO: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting callno to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->callno = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_CAUSE: + ies->cause = (char *)data + 2; + break; + case IAX_IE_CAUSECODE: + if (len != 1) { + snprintf(tmp, (int)sizeof(tmp), "Expecting causecode to be single byte but was %d\n", len); + errorf(tmp); + } else { + ies->causecode = data[2]; + } + break; + case IAX_IE_IAX_UNKNOWN: + if (len == 1) + ies->iax_unknown = data[2]; + else { + snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len); + errorf(tmp); + } + break; + case IAX_IE_MSGCOUNT: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->msgcount = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_AUTOANSWER: + ies->autoanswer = 1; + break; + case IAX_IE_MUSICONHOLD: + ies->musiconhold = 1; + break; + case IAX_IE_TRANSFERID: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting transferid to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->transferid = ntohl(get_unaligned_uint32(data + 2)); + break; + case IAX_IE_DATETIME: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting date/time to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->datetime = ntohl(get_unaligned_uint32(data + 2)); + break; + case IAX_IE_FIRMWAREVER: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting firmwarever to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->firmwarever = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_DEVICETYPE: + ies->devicetype = (char *)data + 2; + break; + case IAX_IE_SERVICEIDENT: + ies->serviceident = (char *)data + 2; + break; + case IAX_IE_FWBLOCKDESC: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected block desc to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->fwdesc = ntohl(get_unaligned_uint32(data + 2)); + break; + case IAX_IE_FWBLOCKDATA: + ies->fwdata = data + 2; + ies->fwdatalen = len; + break; + case IAX_IE_ENCKEY: + ies->enckey = data + 2; + ies->enckeylen = len; + break; + case IAX_IE_PROVVER: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected provisioning version to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->provverpres = 1; + ies->provver = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_CALLINGPRES: + if (len == 1) + ies->calling_pres = data[2]; + else { + snprintf(tmp, (int)sizeof(tmp), "Expected single byte callingpres, but was %d long\n", len); + errorf(tmp); + } + break; + case IAX_IE_CALLINGTON: + if (len == 1) + ies->calling_ton = data[2]; + else { + snprintf(tmp, (int)sizeof(tmp), "Expected single byte callington, but was %d long\n", len); + errorf(tmp); + } + break; + case IAX_IE_CALLINGTNS: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting callingtns to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->calling_tns = ntohs(get_unaligned_uint16(data + 2)); + break; + case IAX_IE_RR_JITTER: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected jitter rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_jitter = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_RR_LOSS: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_loss = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_RR_PKTS: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_pkts = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_RR_DELAY: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else { + ies->rr_delay = ntohs(get_unaligned_uint16(data + 2)); + } + break; + case IAX_IE_RR_DROPPED: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_dropped = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_RR_OOO: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2)); + } + break; + case IAX_IE_VARIABLE: + ast_copy_string(tmp, (char *)data + 2, len + 1); + tmp2 = strchr(tmp, '='); + if (tmp2) + *tmp2++ = '\0'; + else + tmp2 = ""; + { + struct ast_str *str = ast_str_create(16); + /* Existing variable or new variable? */ + for (var2 = ies->vars, prev = NULL; var2; prev = var2, var2 = var2->next) { + if (strcmp(tmp, var2->name) == 0) { + ast_str_set(&str, 0, "%s%s", var2->value, tmp2); + var = ast_variable_new(tmp, ast_str_buffer(str), var2->file); + var->next = var2->next; + if (prev) { + prev->next = var; + } else { + ies->vars = var; + } + snprintf(tmp, sizeof(tmp), "Assigned (%p)%s to (%p)%s\n", var->name, var->name, var->value, var->value); + outputf(tmp); + ast_free(var2); + break; + } + } + ast_free(str); + } + + if (!var2) { + var = ast_variable_new(tmp, tmp2, ""); + snprintf(tmp, sizeof(tmp), "Assigned (%p)%s to (%p)%s\n", var->name, var->name, var->value, var->value); + outputf(tmp); + var->next = ies->vars; + ies->vars = var; + } + break; + case IAX_IE_OSPTOKEN: + if ((count = data[2]) < IAX_MAX_OSPBLOCK_NUM) { + ies->osptokenblock[count] = (char *)data + 2 + 1; + ies->ospblocklength[count] = len - 1; + } else { + snprintf(tmp, (int)sizeof(tmp), "Expected OSP token block index to be 0~%d but was %d\n", IAX_MAX_OSPBLOCK_NUM - 1, count); + errorf(tmp); + } + break; + case IAX_IE_CALLTOKEN: + if (len) { + ies->calltokendata = (unsigned char *) data + 2; + } + ies->calltoken = 1; + break; + default: + snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len); + outputf(tmp); + } + /* Overwrite information element with 0, to null terminate previous portion */ + data[0] = 0; + datalen -= (len + 2); + data += (len + 2); + } + /* Null-terminate last field */ + *data = '\0'; + if (datalen) { + errorf("Invalid information element contents, strange boundary\n"); + return -1; + } + return 0; +} + +void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f) +{ + fr->af.frametype = f->frametype; + ast_format_copy(&fr->af.subclass.format, &f->subclass.format); + fr->af.mallocd = 0; /* Our frame is static relative to the container */ + fr->af.datalen = f->datalen; + fr->af.samples = f->samples; + fr->af.offset = AST_FRIENDLY_OFFSET; + fr->af.src = f->src; + fr->af.delivery.tv_sec = 0; + fr->af.delivery.tv_usec = 0; + fr->af.data.ptr = fr->afdata; + fr->af.len = f->len; + if (fr->af.datalen) { + size_t copy_len = fr->af.datalen; + if (copy_len > fr->afdatalen) { + ast_log(LOG_ERROR, "Losing frame data because destination buffer size '%d' bytes not big enough for '%d' bytes in the frame\n", + (int) fr->afdatalen, (int) fr->af.datalen); + copy_len = fr->afdatalen; + } +#if __BYTE_ORDER == __LITTLE_ENDIAN + /* We need to byte-swap slinear samples from network byte order */ + if ((fr->af.frametype == AST_FRAME_VOICE) && (fr->af.subclass.format.id == AST_FORMAT_SLINEAR)) { + /* 2 bytes / sample for SLINEAR */ + ast_swapcopy_samples(fr->af.data.ptr, f->data.ptr, copy_len / 2); + } else +#endif + memcpy(fr->af.data.ptr, f->data.ptr, copy_len); + } +} + +struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable) +{ + struct iax_frame *fr; + +#if !defined(LOW_MEMORY) + if (cacheable) { + struct iax_frames *iax_frames; + struct iax_frame *smallest; + + /* Attempt to get a frame from this thread's cache */ + if ((iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) { + smallest = AST_LIST_FIRST(&iax_frames->list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&iax_frames->list, fr, list) { + if (fr->afdatalen >= datalen) { + size_t afdatalen = fr->afdatalen; + AST_LIST_REMOVE_CURRENT(list); + iax_frames->size--; + memset(fr, 0, sizeof(*fr)); + fr->afdatalen = afdatalen; + break; + } else if (smallest->afdatalen > fr->afdatalen) { + smallest = fr; + } + } + AST_LIST_TRAVERSE_SAFE_END; + if (!fr) { + if (iax_frames->size >= FRAME_CACHE_MAX_SIZE && smallest) { + /* Make useless cache into something more useful */ + AST_LIST_REMOVE(&iax_frames->list, smallest, list); + iax_frames->size--; + ast_free(smallest); + } + if (!(fr = ast_calloc_cache(1, sizeof(*fr) + datalen))) { + return NULL; + } + fr->afdatalen = datalen; + } + } else { + if (!(fr = ast_calloc_cache(1, sizeof(*fr) + datalen))) { + return NULL; + } + fr->afdatalen = datalen; + } + fr->cacheable = 1; + } else +#endif + { + if (!(fr = ast_calloc(1, sizeof(*fr) + datalen))) { + return NULL; + } + fr->afdatalen = datalen; + } + + + fr->direction = direction; + fr->retrans = -1; + + if (fr->direction == DIRECTION_INGRESS) + ast_atomic_fetchadd_int(&iframes, 1); + else + ast_atomic_fetchadd_int(&oframes, 1); + + ast_atomic_fetchadd_int(&frames, 1); + + return fr; +} + +void iax_frame_free(struct iax_frame *fr) +{ +#if !defined(LOW_MEMORY) + struct iax_frames *iax_frames = NULL; +#endif + + /* Note: does not remove from scheduler! */ + if (fr->direction == DIRECTION_INGRESS) + ast_atomic_fetchadd_int(&iframes, -1); + else if (fr->direction == DIRECTION_OUTGRESS) + ast_atomic_fetchadd_int(&oframes, -1); + else { + errorf("Attempt to double free frame detected\n"); + return; + } + ast_atomic_fetchadd_int(&frames, -1); + +#if !defined(LOW_MEMORY) + if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) { + ast_free(fr); + return; + } + + if (iax_frames->size < FRAME_CACHE_MAX_SIZE) { + fr->direction = 0; + /* Pseudo-sort: keep smaller frames at the top of the list. This should + * increase the chance that we pick the smallest applicable frame for use. */ + if (AST_LIST_FIRST(&iax_frames->list) && AST_LIST_FIRST(&iax_frames->list)->afdatalen < fr->afdatalen) { + AST_LIST_INSERT_TAIL(&iax_frames->list, fr, list); + } else { + AST_LIST_INSERT_HEAD(&iax_frames->list, fr, list); + } + iax_frames->size++; + return; + } +#endif + ast_free(fr); +} + +#if !defined(LOW_MEMORY) +static void frame_cache_cleanup(void *data) +{ + struct iax_frames *framelist = data; + struct iax_frame *current; + + while ((current = AST_LIST_REMOVE_HEAD(&framelist->list, list))) + ast_free(current); + + ast_free(framelist); +} +#endif + +int iax_get_frames(void) { return frames; } +int iax_get_iframes(void) { return iframes; } +int iax_get_oframes(void) { return oframes; } diff --git a/channels/iax2/provision.c b/channels/iax2/provision.c new file mode 100644 index 000000000..78e0b1b3d --- /dev/null +++ b/channels/iax2/provision.c @@ -0,0 +1,566 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief IAX Provisioning Protocol + * + * \author Mark Spencer <markster@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <sys/socket.h> + +#include "asterisk/config.h" +#include "asterisk/cli.h" +#include "asterisk/lock.h" +#include "asterisk/frame.h" +#include "asterisk/md5.h" +#include "asterisk/astdb.h" +#include "asterisk/utils.h" +#include "asterisk/acl.h" + +#include "include/iax2.h" +#include "include/provision.h" +#include "include/parser.h" + +static int provinit = 0; + +struct iax_template { + int dead; + char name[80]; + char src[80]; + char user[20]; + char pass[20]; + char lang[10]; + unsigned short port; + unsigned int server; + unsigned short serverport; + unsigned int altserver; + unsigned int flags; + iax2_format format; + unsigned int tos; + AST_LIST_ENTRY(iax_template) list; +}; + +static AST_LIST_HEAD_NOLOCK_STATIC(templates, iax_template); + +AST_MUTEX_DEFINE_STATIC(provlock); + +static struct iax_flag { + char *name; + int value; +} iax_flags[] = { + { "register", PROV_FLAG_REGISTER }, + { "secure", PROV_FLAG_SECURE }, + { "heartbeat", PROV_FLAG_HEARTBEAT }, + { "debug", PROV_FLAG_DEBUG }, + { "disablecid", PROV_FLAG_DIS_CALLERID }, + { "disablecw", PROV_FLAG_DIS_CALLWAIT }, + { "disablecidcw", PROV_FLAG_DIS_CIDCW }, + { "disable3way", PROV_FLAG_DIS_THREEWAY }, +}; + +char *iax_provflags2str(char *buf, int buflen, unsigned int flags) +{ + int x; + + if (!buf || buflen < 1) + return NULL; + + buf[0] = '\0'; + + for (x = 0; x < ARRAY_LEN(iax_flags); x++) { + if (flags & iax_flags[x].value){ + strncat(buf, iax_flags[x].name, buflen - strlen(buf) - 1); + strncat(buf, ",", buflen - strlen(buf) - 1); + } + } + + if (!ast_strlen_zero(buf)) + buf[strlen(buf) - 1] = '\0'; + else + strncpy(buf, "none", buflen - 1); + + return buf; +} + +static unsigned int iax_str2flags(const char *buf) +{ + int x; + int len; + unsigned int flags = 0; + char *e; + while(buf && *buf) { + e = strchr(buf, ','); + if (e) + len = e - buf; + else + len = 0; + for (x = 0; x < ARRAY_LEN(iax_flags); x++) { + if ((len && !strncasecmp(iax_flags[x].name, buf, len)) || + (!len && !strcasecmp(iax_flags[x].name, buf))) { + flags |= iax_flags[x].value; + break; + } + } + if (e) { + buf = e + 1; + while(*buf && (*buf < 33)) + buf++; + } else + break; + } + return flags; +} + +static void iax_template_copy(struct iax_template *dst, struct iax_template *src) +{ + if (!dst || !src) { + return; + } + + dst->dead = src->dead; + ast_copy_string(dst->name, src->name, sizeof(dst->name)); + ast_copy_string(dst->src, src->src, sizeof(dst->src)); + ast_copy_string(dst->user, src->user, sizeof(dst->user)); + ast_copy_string(dst->pass, src->pass, sizeof(dst->pass)); + ast_copy_string(dst->lang, src->lang, sizeof(dst->lang)); + dst->port = src->port; + dst->server = src->server; + dst->altserver = src->altserver; + dst->flags = src->flags; + dst->format = src->format; + dst->tos = src->tos; +} + +static struct iax_template *iax_template_find(const char *s, int allowdead) +{ + struct iax_template *cur; + + AST_LIST_TRAVERSE(&templates, cur, list) { + if (!strcasecmp(s, cur->name)) { + if (!allowdead && cur->dead) { + cur = NULL; + } + break; + } + } + + return cur; +} + +char *iax_prov_complete_template(const char *line, const char *word, int pos, int state) +{ + struct iax_template *c; + int which=0; + char *ret = NULL; + int wordlen = strlen(word); + + if (pos == 3) { + ast_mutex_lock(&provlock); + AST_LIST_TRAVERSE(&templates, c, list) { + if (!strncasecmp(word, c->name, wordlen) && ++which > state) { + ret = ast_strdup(c->name); + break; + } + } + ast_mutex_unlock(&provlock); + } + return ret; +} + +static unsigned int prov_ver_calc(struct iax_ie_data *provdata) +{ + struct MD5Context md5; + unsigned int tmp[4]; + MD5Init(&md5); + MD5Update(&md5, provdata->buf, provdata->pos); + MD5Final((unsigned char *)tmp, &md5); + return tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3]; +} + +int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force) +{ + struct iax_template *cur; + unsigned int sig; + char tmp[40]; + memset(provdata, 0, sizeof(*provdata)); + ast_mutex_lock(&provlock); + cur = iax_template_find(template, 1); + /* If no match, try searching for '*' */ + if (!cur) + cur = iax_template_find("*", 1); + if (cur) { + /* found it -- add information elements as appropriate */ + if (force || strlen(cur->user)) + iax_ie_append_str(provdata, PROV_IE_USER, cur->user); + if (force || strlen(cur->pass)) + iax_ie_append_str(provdata, PROV_IE_PASS, cur->pass); + if (force || strlen(cur->lang)) + iax_ie_append_str(provdata, PROV_IE_LANG, cur->lang); + if (force || cur->port) + iax_ie_append_short(provdata, PROV_IE_PORTNO, cur->port); + if (force || cur->server) + iax_ie_append_int(provdata, PROV_IE_SERVERIP, cur->server); + if (force || cur->serverport) + iax_ie_append_short(provdata, PROV_IE_SERVERPORT, cur->serverport); + if (force || cur->altserver) + iax_ie_append_int(provdata, PROV_IE_ALTSERVER, cur->altserver); + if (force || cur->flags) + iax_ie_append_int(provdata, PROV_IE_FLAGS, cur->flags); + if (force || cur->format) + iax_ie_append_int(provdata, PROV_IE_FORMAT, cur->format); + if (force || cur->tos) + iax_ie_append_byte(provdata, PROV_IE_TOS, cur->tos); + + /* Calculate checksum of message so far */ + sig = prov_ver_calc(provdata); + if (signature) + *signature = sig; + /* Store signature */ + iax_ie_append_int(provdata, PROV_IE_PROVVER, sig); + /* Cache signature for later verification so we need not recalculate all this */ + snprintf(tmp, sizeof(tmp), "v0x%08x", sig); + ast_db_put("iax/provisioning/cache", template, tmp); + } else + ast_db_put("iax/provisioning/cache", template, "u"); + ast_mutex_unlock(&provlock); + return cur ? 0 : -1; +} + +int iax_provision_version(unsigned int *version, const char *template, int force) +{ + char tmp[80] = ""; + struct iax_ie_data ied; + int ret=0; + memset(&ied, 0, sizeof(ied)); + + ast_mutex_lock(&provlock); + if (ast_db_get("iax/provisioning/cache", template, tmp, sizeof(tmp))) { + ast_log(LOG_ERROR, "ast_db_get failed to retrieve iax/provisioning/cache/%s\n", template); + } + if (sscanf(tmp, "v%30x", version) != 1) { + if (strcmp(tmp, "u")) { + ret = iax_provision_build(&ied, version, template, force); + if (ret) + ast_debug(1, "Unable to create provisioning packet for '%s'\n", template); + } else + ret = -1; + } else + ast_debug(1, "Retrieved cached version '%s' = '%08x'\n", tmp, *version); + ast_mutex_unlock(&provlock); + return ret; +} + +static int iax_template_parse(struct iax_template *cur, struct ast_config *cfg, const char *s, const char *def) +{ + struct ast_variable *v; + int foundportno = 0; + int foundserverportno = 0; + int x; + struct in_addr ia; + struct hostent *hp; + struct ast_hostent h; + struct iax_template *src, tmp; + const char *t; + if (def) { + t = ast_variable_retrieve(cfg, s ,"template"); + src = NULL; + if (t && strlen(t)) { + src = iax_template_find(t, 0); + if (!src) + ast_log(LOG_WARNING, "Unable to find base template '%s' for creating '%s'. Trying '%s'\n", t, s, def); + else + def = t; + } + if (!src) { + src = iax_template_find(def, 0); + if (!src) + ast_log(LOG_WARNING, "Unable to locate default base template '%s' for creating '%s', omitting.\n", def, s); + } + if (!src) + return -1; + ast_mutex_lock(&provlock); + /* Backup old data */ + iax_template_copy(&tmp, cur); + /* Restore from src */ + iax_template_copy(cur, src); + /* Restore important headers */ + memcpy(cur->name, tmp.name, sizeof(cur->name)); + cur->dead = tmp.dead; + ast_mutex_unlock(&provlock); + } + if (def) + ast_copy_string(cur->src, def, sizeof(cur->src)); + else + cur->src[0] = '\0'; + v = ast_variable_browse(cfg, s); + while(v) { + if (!strcasecmp(v->name, "port") || !strcasecmp(v->name, "serverport")) { + if ((sscanf(v->value, "%5d", &x) == 1) && (x > 0) && (x < 65535)) { + if (!strcasecmp(v->name, "port")) { + cur->port = x; + foundportno = 1; + } else { + cur->serverport = x; + foundserverportno = 1; + } + } else + ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno); + } else if (!strcasecmp(v->name, "server") || !strcasecmp(v->name, "altserver")) { + hp = ast_gethostbyname(v->value, &h); + if (hp) { + memcpy(&ia, hp->h_addr, sizeof(ia)); + if (!strcasecmp(v->name, "server")) + cur->server = ntohl(ia.s_addr); + else + cur->altserver = ntohl(ia.s_addr); + } else + ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno); + } else if (!strcasecmp(v->name, "codec")) { + struct ast_format tmpfmt; + if ((ast_getformatbyname(v->value, &tmpfmt)) > 0) { + cur->format = ast_format_to_old_bitfield(&tmpfmt); + } else + ast_log(LOG_WARNING, "Ignoring invalid codec '%s' for '%s' at line %d\n", v->value, s, v->lineno); + } else if (!strcasecmp(v->name, "tos")) { + if (ast_str2tos(v->value, &cur->tos)) + ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno); + } else if (!strcasecmp(v->name, "user")) { + ast_copy_string(cur->user, v->value, sizeof(cur->user)); + if (strcmp(cur->user, v->value)) + ast_log(LOG_WARNING, "Truncating username from '%s' to '%s' for '%s' at line %d\n", v->value, cur->user, s, v->lineno); + } else if (!strcasecmp(v->name, "pass")) { + ast_copy_string(cur->pass, v->value, sizeof(cur->pass)); + if (strcmp(cur->pass, v->value)) + ast_log(LOG_WARNING, "Truncating password from '%s' to '%s' for '%s' at line %d\n", v->value, cur->pass, s, v->lineno); + } else if (!strcasecmp(v->name, "language")) { + ast_copy_string(cur->lang, v->value, sizeof(cur->lang)); + if (strcmp(cur->lang, v->value)) + ast_log(LOG_WARNING, "Truncating language from '%s' to '%s' for '%s' at line %d\n", v->value, cur->lang, s, v->lineno); + } else if (!strcasecmp(v->name, "flags")) { + cur->flags = iax_str2flags(v->value); + } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '+')) { + cur->flags |= iax_str2flags(v->value); + } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '-')) { + cur->flags &= ~iax_str2flags(v->value); + } else if (strcasecmp(v->name, "template")) { + ast_log(LOG_WARNING, "Unknown keyword '%s' in definition of '%s' at line %d\n", v->name, s, v->lineno); + } + v = v->next; + } + if (!foundportno) + cur->port = IAX_DEFAULT_PORTNO; + if (!foundserverportno) + cur->serverport = IAX_DEFAULT_PORTNO; + return 0; +} + +static int iax_process_template(struct ast_config *cfg, char *s, char *def) +{ + /* Find an already existing one if there */ + struct iax_template *cur; + int mallocd = 0; + + cur = iax_template_find(s, 1 /* allow dead */); + if (!cur) { + mallocd = 1; + cur = ast_calloc(1, sizeof(*cur)); + if (!cur) { + ast_log(LOG_WARNING, "Out of memory!\n"); + return -1; + } + /* Initialize entry */ + ast_copy_string(cur->name, s, sizeof(cur->name)); + cur->dead = 1; + } + if (!iax_template_parse(cur, cfg, s, def)) + cur->dead = 0; + + /* Link if we're mallocd */ + if (mallocd) { + ast_mutex_lock(&provlock); + AST_LIST_INSERT_HEAD(&templates, cur, list); + ast_mutex_unlock(&provlock); + } + return 0; +} + +static const char *ifthere(const char *s) +{ + if (strlen(s)) + return s; + else + return "<unspecified>"; +} + +static const char *iax_server(unsigned int addr) +{ + struct in_addr ia; + + if (!addr) + return "<unspecified>"; + + ia.s_addr = htonl(addr); + + return ast_inet_ntoa(ia); +} + + +static char *iax_show_provisioning(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct iax_template *cur; + char server[INET_ADDRSTRLEN]; + char alternate[INET_ADDRSTRLEN]; + char flags[80]; /* Has to be big enough for 'flags' too */ + int found = 0; + + switch (cmd) { + case CLI_INIT: + e->command = "iax2 show provisioning"; + e->usage = + "Usage: iax2 show provisioning [template]\n" + " Lists all known IAX provisioning templates or a\n" + " specific one if specified.\n"; + return NULL; + case CLI_GENERATE: + return iax_prov_complete_template(a->line, a->word, a->pos, a->n); + } + + if ((a->argc != 3) && (a->argc != 4)) + return CLI_SHOWUSAGE; + + ast_mutex_lock(&provlock); + AST_LIST_TRAVERSE(&templates, cur, list) { + if ((a->argc == 3) || (!strcasecmp(a->argv[3], cur->name))) { + if (found) + ast_cli(a->fd, "\n"); + ast_copy_string(server, iax_server(cur->server), sizeof(server)); + ast_copy_string(alternate, iax_server(cur->altserver), sizeof(alternate)); + ast_cli(a->fd, "== %s ==\n", cur->name); + ast_cli(a->fd, "Base Templ: %s\n", strlen(cur->src) ? cur->src : "<none>"); + ast_cli(a->fd, "Username: %s\n", ifthere(cur->user)); + ast_cli(a->fd, "Secret: %s\n", ifthere(cur->pass)); + ast_cli(a->fd, "Language: %s\n", ifthere(cur->lang)); + ast_cli(a->fd, "Bind Port: %d\n", cur->port); + ast_cli(a->fd, "Server: %s\n", server); + ast_cli(a->fd, "Server Port: %d\n", cur->serverport); + ast_cli(a->fd, "Alternate: %s\n", alternate); + ast_cli(a->fd, "Flags: %s\n", iax_provflags2str(flags, sizeof(flags), cur->flags)); + ast_cli(a->fd, "Format: %s\n", iax2_getformatname(cur->format)); + ast_cli(a->fd, "TOS: 0x%x\n", cur->tos); + found++; + } + } + ast_mutex_unlock(&provlock); + if (!found) { + if (a->argc == 3) + ast_cli(a->fd, "No provisioning templates found\n"); + else + ast_cli(a->fd, "No provisioning template matching '%s' found\n", a->argv[3]); + } + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_iax2_provision[] = { + AST_CLI_DEFINE(iax_show_provisioning, "Display iax provisioning"), +}; + +static int iax_provision_init(void) +{ + ast_cli_register_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry)); + provinit = 1; + return 0; +} + +static void iax_provision_free_templates(int dead) +{ + struct iax_template *cur; + + /* Drop dead or not (depending on dead) entries while locked */ + ast_mutex_lock(&provlock); + AST_LIST_TRAVERSE_SAFE_BEGIN(&templates, cur, list) { + if ((dead && cur->dead) || !dead) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(cur); + } + } + AST_LIST_TRAVERSE_SAFE_END; + ast_mutex_unlock(&provlock); +} + +int iax_provision_unload(void) +{ + provinit = 0; + ast_cli_unregister_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry)); + iax_provision_free_templates(0 /* Remove all templates. */); + + return 0; +} + +int iax_provision_reload(int reload) +{ + struct ast_config *cfg; + struct iax_template *cur; + char *cat; + int found = 0; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + if (!provinit) + iax_provision_init(); + + cfg = ast_config_load2("iaxprov.conf", "chan_iax2", config_flags); + if (cfg != NULL && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID) { + /* Mark all as dead. No need for locking */ + AST_LIST_TRAVERSE(&templates, cur, list) { + cur->dead = 1; + } + + /* Load as appropriate */ + cat = ast_category_browse(cfg, NULL); + while(cat) { + if (strcasecmp(cat, "general")) { + iax_process_template(cfg, cat, found ? "default" : NULL); + found++; + ast_verb(3, "Loaded provisioning template '%s'\n", cat); + } + cat = ast_category_browse(cfg, cat); + } + ast_config_destroy(cfg); + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) + return 0; + else + ast_log(LOG_NOTICE, "No IAX provisioning configuration found, IAX provisioning disabled.\n"); + + iax_provision_free_templates(1 /* remove only marked as dead */); + + /* Purge cached signature DB entries */ + ast_db_deltree("iax/provisioning/cache", NULL); + return 0; +} |