summaryrefslogtreecommitdiff
path: root/channels/iax2
diff options
context:
space:
mode:
authorSean Bright <sean@malleable.com>2013-01-29 22:58:33 +0000
committerSean Bright <sean@malleable.com>2013-01-29 22:58:33 +0000
commit693d609081031d17e57cc2b3ad7408947120dc48 (patch)
tree8057900da93bb4d8111512cf3d4e17475996ff29 /channels/iax2
parentffaf79b1eb7e7a91d2262b5e981ed4fa7142f9a9 (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.h301
-rw-r--r--channels/iax2/include/parser.h177
-rw-r--r--channels/iax2/include/provision.h58
-rw-r--r--channels/iax2/parser.c1294
-rw-r--r--channels/iax2/provision.c566
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;
+}