Skip to content
Snippets Groups Projects
chan_sip.c 938 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	int lastmsgssent;
    
    	unsigned int sipoptions;	/*!<  Supported SIP options */
    
    	struct ast_flags flags[2];	/*!<  SIP_ flags */
    
    	/*! Mailboxes that this peer cares about */
    	AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes;
    
    	int maxcallbitrate;		/*!< Maximum Bitrate for a video call */
    
    	int expire;			/*!<  When to expire this peer registration */
    	int capability;			/*!<  Codec capability */
    	int rtptimeout;			/*!<  RTP timeout */
    	int rtpholdtimeout;		/*!<  RTP Hold Timeout */
    	int rtpkeepalive;		/*!<  Send RTP packets for keepalive */
    	ast_group_t callgroup;		/*!<  Call group */
    	ast_group_t pickupgroup;	/*!<  Pickup group */
    
    	struct sip_proxy *outboundproxy;	/*!< Outbound proxy for this peer */
    
    	struct ast_dnsmgr_entry *dnsmgr;/*!<  DNS refresh manager for peer */
    	struct sockaddr_in addr;	/*!<  IP address of peer */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Qualification */
    
    	struct sip_pvt *call;		/*!<  Call pointer */
    	int pokeexpire;			/*!<  When to expire poke (qualify= checking) */
    	int lastms;			/*!<  How long last response took (in ms), or -1 for no response */
    	int maxms;			/*!<  Max ms we will accept for the host to be up, 0 to not monitor */
    
    	int qualifyfreq;		/*!<  Qualification: How often to check for the host to be up */
    
    	struct timeval ps;		/*!<  Time for sending SIP OPTION in sip_pke_peer() */
    
    	struct sockaddr_in defaddr;	/*!<  Default IP address, used until registration */
    	struct ast_ha *ha;		/*!<  Access control list */
    
    	struct ast_ha *contactha;       /*!<  Restrict what IPs are allowed in the Contact header (for registration) */
    
    	struct ast_variable *chanvars;	/*!<  Variables to set for channel created by user */
    
    	struct sip_pvt *mwipvt;		/*!<  Subscription for MWI */
    
    	struct sip_st_cfg stimer;	/*!<  SIP Session-Timers */
    	int timer_t1;			/*!<  The maximum T1 value for the peer */
    	int timer_b;			/*!<  The maximum timer B (transaction timeouts) */
    
    	int deprecated_username; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */
    
    	
    	/*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */
    
    	enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
    
    /*! 
     * \brief Registrations with other SIP proxies
     *
    
     * Created by sip_register(), the entry is linked in the 'regl' list,
     * and never deleted (other than at 'sip reload' or module unload times).
     * The entry always has a pending timeout, either waiting for an ACK to
     * the REGISTER message (in which case we have to retransmit the request),
     * or waiting for the next REGISTER message to be sent (either the initial one,
     * or once the previously completed registration one expires).
     * The registration can be in one of many states, though at the moment
     * the handling is a bit mixed.
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct sip_registry {
    
    	ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1);
    
    	AST_DECLARE_STRING_FIELDS(
    		AST_STRING_FIELD(callid);	/*!< Global Call-ID */
    		AST_STRING_FIELD(realm);	/*!< Authorization realm */
    		AST_STRING_FIELD(nonce);	/*!< Authorization nonce */
    		AST_STRING_FIELD(opaque);	/*!< Opaque nonsense */
    		AST_STRING_FIELD(qop);		/*!< Quality of Protection, since SIP wasn't complicated enough yet. */
    
    		AST_STRING_FIELD(authdomain);	/*!< Authorization domain */
    		AST_STRING_FIELD(regdomain);	/*!< Registration domain */
    
    		AST_STRING_FIELD(username);	/*!< Who we are registering as */
    		AST_STRING_FIELD(authuser);	/*!< Who we *authenticate* as */
    		AST_STRING_FIELD(hostname);	/*!< Domain or host we register to */
    		AST_STRING_FIELD(secret);	/*!< Password in clear text */	
    		AST_STRING_FIELD(md5secret);	/*!< Password in md5 */
    
    		AST_STRING_FIELD(callback);	/*!< Contact extension */
    
    		AST_STRING_FIELD(peername);	/*!< Peer registering to */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	enum sip_transport transport;	/*!< Transport for this registration UDP, TCP or TLS */
    
    	int portno;			/*!<  Optional port override */
    	int expire;			/*!< Sched ID of expiration */
    
    	int configured_expiry;		/*!< Configured value to use for the Expires header */
    	int expiry;			/*!< Negotiated value used for the Expires header */
    
    	int regattempts;		/*!< Number of attempts (since the last success) */
    	int timeout; 			/*!< sched id of sip_reg_timeout */
    	int refresh;			/*!< How often to refresh */
    
    	struct sip_pvt *call;		/*!< create a sip_pvt structure for each outbound "registration dialog" in progress */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	enum sipregistrystate regstate;	/*!< Registration state (see above) */
    
    	struct timeval regtime;		/*!< Last successful registration time */
    
    	int callid_valid;		/*!< 0 means we haven't chosen callid for this registry yet. */
    	unsigned int ocseq;		/*!< Sequence number we got to for REGISTERs for this registry */
    
    	struct ast_dnsmgr_entry *dnsmgr;	/*!<  DNS refresh manager for register */
    	struct sockaddr_in us;		/*!< Who the server thinks we are */
    
    	int noncecount;			/*!< Nonce-count */
    
    	char lastmsg[256];		/*!< Last Message sent/received */
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Definition of a thread that handles a socket */
    
    struct sip_threadinfo {
    	int stop;
    	pthread_t threadid;
    
    	struct ast_tcptls_session_instance *tcptls_session;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	enum sip_transport type;	/*!< We keep a copy of the type here so we can display it in the connection list */
    
    	AST_LIST_ENTRY(sip_threadinfo) list;
    };
    
    
    /*! \brief Definition of an MWI subscription to another server */
    struct sip_subscription_mwi {
    	ASTOBJ_COMPONENTS_FULL(struct sip_subscription_mwi,1,1);
    	AST_DECLARE_STRING_FIELDS(
    		AST_STRING_FIELD(username);     /*!< Who we are sending the subscription as */
    		AST_STRING_FIELD(authuser);     /*!< Who we *authenticate* as */
    		AST_STRING_FIELD(hostname);     /*!< Domain or host we subscribe to */
    		AST_STRING_FIELD(secret);       /*!< Password in clear text */
    		AST_STRING_FIELD(mailbox);      /*!< Mailbox store to put MWI into */
    		);
    	enum sip_transport transport;    /*!< Transport to use */
    	int portno;                      /*!< Optional port override */
    	int resub;                       /*!< Sched ID of resubscription */
    	unsigned int subscribed:1;       /*!< Whether we are currently subscribed or not */
    	struct sip_pvt *call;            /*!< Outbound subscription dialog */
    	struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager for subscription */
    	struct sockaddr_in us;           /*!< Who the server thinks we are */
    };
    
    
    /* --- Hash tables of various objects --------*/
    
    #ifdef LOW_MEMORY
    static int hash_peer_size = 17;
    static int hash_dialog_size = 17;
    static int hash_user_size = 17;
    #else
    
    Olle Johansson's avatar
    Olle Johansson committed
    static int hash_peer_size = 563;	/*!< Size of peer hash table, prime number preferred! */
    
    static int hash_dialog_size = 563;
    static int hash_user_size = 563;
    #endif
    
    /*! \brief  The thread list of TCP threads */
    static AST_LIST_HEAD_STATIC(threadl, sip_threadinfo);
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief  The peer list: Users, Peers and Friends */
    
    static struct ao2_container *peers;
    static struct ao2_container *peers_by_ip;
    
    /*! \brief  The register list: Other SIP proxies we register with and place calls to */
    
    static struct ast_register_list {
    
    	ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry);
    
    /*! \brief  The MWI subscription list */
    static struct ast_subscription_mwi_list {
    	ASTOBJ_CONTAINER_COMPONENTS(struct sip_subscription_mwi);
    } submwil;
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief
    
     * \note The only member of the peer used here is the name field
     */
    static int peer_hash_cb(const void *obj, const int flags)
    {
    	const struct sip_peer *peer = obj;
    
    
    	return ast_str_case_hash(peer->name);
    
    }
    
    /*!
     * \note The only member of the peer used here is the name field
     */
    
    static int peer_cmp_cb(void *obj, void *arg, int flags)
    
    {
    	struct sip_peer *peer = obj, *peer2 = arg;
    
    
    	return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH | CMP_STOP : 0;
    
    }
    
    /*!
     * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
     */
    static int peer_iphash_cb(const void *obj, const int flags)
    {
    	const struct sip_peer *peer = obj;
    	int ret1 = peer->addr.sin_addr.s_addr;
    	if (ret1 < 0)
    		ret1 = -ret1;
    	
    	if (ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT)) {
    		return ret1;
    	} else {
    		return ret1 + peer->addr.sin_port;
    	}
    }
    
    /*!
     * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
     */
    
    static int peer_ipcmp_cb(void *obj, void *arg, int flags)
    
    {
    	struct sip_peer *peer = obj, *peer2 = arg;
    
    	if (peer->addr.sin_addr.s_addr != peer2->addr.sin_addr.s_addr)
    		return 0;
    	
    	if (!ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) && !ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) {
    		if (peer->addr.sin_port == peer2->addr.sin_port)
    
    			return CMP_MATCH | CMP_STOP;
    
    	return CMP_MATCH | CMP_STOP;
    
    }
    
    /*!
     * \note The only member of the dialog used here callid string
     */
    static int dialog_hash_cb(const void *obj, const int flags)
    {
    	const struct sip_pvt *pvt = obj;
    
    
    	return ast_str_case_hash(pvt->callid);
    
    }
    
    /*!
     * \note The only member of the dialog used here callid string
     */
    
    static int dialog_cmp_cb(void *obj, void *arg, int flags)
    
    	return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH | CMP_STOP : 0;
    
    /*! \brief A per-thread temporary pvt structure */
    
    AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
    
    #ifdef LOW_MEMORY
    
    static void ts_ast_rtp_destroy(void *);
    
    AST_THREADSTORAGE_CUSTOM(ts_audio_rtp, NULL, ts_ast_rtp_destroy);
    AST_THREADSTORAGE_CUSTOM(ts_video_rtp, NULL, ts_ast_rtp_destroy);
    AST_THREADSTORAGE_CUSTOM(ts_text_rtp, NULL, ts_ast_rtp_destroy);
    
    /*! \brief Authentication list for realm authentication 
    
     * \todo Move the sip_auth list to AST_LIST */
    static struct sip_auth *authl = NULL;
    
    /* --- Sockets and networking --------------*/
    
    /*! \brief Main socket for UDP SIP communication.
    
     *
     * sipsock is shared between the SIP manager thread (which handles reload
    
     * requests), the udp io handler (sipsock_read()) and the user routines that
     * issue udp writes (using __sip_xmit()).
    
     * The socket is -1 only when opening fails (this is a permanent condition),
     * or when we are handling a reload() that changes its address (this is
     * a transient situation during which we might have a harmless race, see
     * below). Because the conditions for the race to be possible are extremely
     * rare, we don't want to pay the cost of locking on every I/O.
     * Rather, we remember that when the race may occur, communication is
     * bound to fail anyways, so we just live with this event and let
     * the protocol handle this above us.
     */
    static int sipsock  = -1;
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    static struct sockaddr_in bindaddr;	/*!< UDP: The address we bind to */
    
    
    /*! \brief our (internal) default address/port to put in SIP/SDP messages
     *  internip is initialized picking a suitable address from one of the
     * interfaces, and the same port number we bind to. It is used as the
     * default address/port in SIP messages, and as the default address
     * (but not port) in SDP messages.
     */
    static struct sockaddr_in internip;
    
    
    /*! \brief our external IP address/port for SIP sessions.
     * externip.sin_addr is only set when we know we might be behind
     * a NAT, and this is done using a variety of (mutually exclusive)
     * ways from the config file:
     *
     * + with "externip = host[:port]" we specify the address/port explicitly.
     *   The address is looked up only once when (re)loading the config file;
     * 
     * + with "externhost = host[:port]" we do a similar thing, but the
     *   hostname is stored in externhost, and the hostname->IP mapping
     *   is refreshed every 'externrefresh' seconds;
     * 
     * + with "stunaddr = host[:port]" we run queries every externrefresh seconds
     *   to the specified server, and store the result in externip.
     *
     * Other variables (externhost, externexpire, externrefresh) are used
     * to support the above functions.
     */
    
    static struct sockaddr_in externip;		/*!< External IP address if we are behind NAT */
    
    static char externhost[MAXHOSTNAMELEN];		/*!< External host name */
    static time_t externexpire;			/*!< Expiration counter for re-resolving external host name in dynamic DNS */
    
    static struct sockaddr_in stunaddr;		/*!< stun server address */
    
    
    /*! \brief  List of local networks
     * We store "localnet" addresses from the config file into an access list,
     * marked as 'DENY', so the call to ast_apply_ha() will return
     * AST_SENSE_DENY for 'local' addresses, and AST_SENSE_ALLOW for 'non local'
     * (i.e. presumably public) addresses.
     */
    
    static struct ast_ha *localaddr;		/*!< List of local networks, on the same side of NAT as this Asterisk */
    
    static int ourport_tcp;				/*!< The port used for TCP connections */
    static int ourport_tls;				/*!< The port used for TCP/TLS connections */
    
    static struct sockaddr_in debugaddr;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static struct ast_config *notify_types;		/*!< The list of manual NOTIFY types we know how to send */
    
    /*! some list management macros. */
     
    #define UNLINK(element, head, prev) do {	\
    	if (prev)				\
    		(prev)->next = (element)->next;	\
    	else					\
    		(head) = (element)->next;	\
    	} while (0)
    
    
    enum t38_action_flag {
    	SDP_T38_NONE = 0, /*!< Do not modify T38 information at all */
    	SDP_T38_INITIATE, /*!< Remote side has requested T38 with us */
    	SDP_T38_ACCEPT,   /*!< Remote side accepted our T38 request */
    };
    
    
    /*---------------------------- Forward declarations of functions in chan_sip.c */
    
    /* Note: This is added to help splitting up chan_sip.c into several files
    	in coming releases. */
    
    static struct ast_channel *sip_request_call(const char *type, int format, const struct ast_channel *requestor, void *data, int *cause);
    
    static int sip_devicestate(void *data);
    static int sip_sendtext(struct ast_channel *ast, const char *text);
    static int sip_call(struct ast_channel *ast, char *dest, int timeout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen);
    
    static int sip_hangup(struct ast_channel *ast);
    static int sip_answer(struct ast_channel *ast);
    static struct ast_frame *sip_read(struct ast_channel *ast);
    static int sip_write(struct ast_channel *ast, struct ast_frame *frame);
    static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
    static int sip_transfer(struct ast_channel *ast, const char *dest);
    static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
    
    static int sip_senddigit_begin(struct ast_channel *ast, char digit);
    
    static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
    
    static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen);
    
    static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen);
    
    static const char *sip_get_callid(struct ast_channel *chan);
    
    static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin);
    
    static int sip_standard_port(enum sip_transport type, int port);
    
    static int sip_prepare_socket(struct sip_pvt *p);
    
    static int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum sip_transport *transport);
    
    /*--- Transmitting responses and requests */
    
    static int sipsock_read(int *id, int fd, short events, void *ignore);
    
    static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len);
    static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod);
    
    static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
    
    static int retrans_pkt(const void *data);
    
    static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req);
    static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req);
    static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req);
    
    static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp, int rpid);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
    static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
    static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
    
    static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable);
    
    static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
    
    static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch);
    static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init);
    
    static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp);
    
    static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_info_with_vidupdate(struct sip_pvt *p);
    
    static int transmit_message_with_text(struct sip_pvt *p, const char *text);
    static int transmit_refer(struct sip_pvt *p, const char *dest);
    
    static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten);
    
    static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate);
    
    static int transmit_notify_custom(struct sip_pvt *p, struct ast_variable *vars);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader);
    
    static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
    static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno);
    
    static void copy_request(struct sip_request *dst, const struct sip_request *src);
    
    static void receive_message(struct sip_pvt *p, struct sip_request *req);
    
    static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req, char **name, char **number, int set_call_forward);
    
    static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only);
    
    
    /*--- Dialog management */
    static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
    
    David Vossel's avatar
    David Vossel committed
    				 int useglobal_nat, const int intended_method, struct sip_request *req);
    
    static int __sip_autodestruct(const void *data);
    
    static void sip_scheddestroy(struct sip_pvt *p, int ms);
    
    static int sip_cancel_destroy(struct sip_pvt *p);
    
    static struct sip_pvt *sip_destroy(struct sip_pvt *p);
    
    static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist);
    static void *registry_unref(struct sip_registry *reg, char *tag);
    static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist);
    
    static int __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
    
    static void __sip_pretend_ack(struct sip_pvt *p);
    
    static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
    
    static int auto_congest(const void *arg);
    
    static int update_call_counter(struct sip_pvt *fup, int event);
    static int hangup_sip2cause(int cause);
    static const char *hangup_cause2sip(int cause);
    static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method);
    
    static void free_old_route(struct sip_route *route);
    static void list_route(struct sip_route *route);
    static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards);
    static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin,
    
    					      struct sip_request *req, const char *uri);
    
    static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
    static void check_pendings(struct sip_pvt *p);
    static void *sip_park_thread(void *stuff);
    static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno);
    static int sip_sipredirect(struct sip_pvt *p, const char *dest);
    
    
    /*--- Codec handling / SDP */
    static void try_suggested_sip_codec(struct sip_pvt *p);
    static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name);
    static const char *get_sdp(struct sip_request *req, const char *name);
    
    static int find_sdp(struct sip_request *req);
    
    static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action);
    
    static void add_codec_to_sdp(const struct sip_pvt *p, int codec,
    
    			     struct ast_str **m_buf, struct ast_str **a_buf,
    
    			     int debug, int *min_packet_size);
    
    static void add_noncodec_to_sdp(const struct sip_pvt *p, int format,
    
    				struct ast_str **m_buf, struct ast_str **a_buf,
    
    static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp, int add_audio, int add_t38);
    
    static void do_setnat(struct sip_pvt *p);
    
    static void stop_media_flows(struct sip_pvt *p);
    
    Olle Johansson's avatar
    Olle Johansson committed
    static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
    
    static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len);
    
    static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
    					 const char *secret, const char *md5secret, int sipmethod,
    
    					 const char *uri, enum xmittype reliable, int ignore);
    
    static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
    
    					      int sipmethod, const char *uri, enum xmittype reliable,
    
    					      struct sockaddr_in *sin, struct sip_peer **authpeer);
    
    static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, const char *uri, enum xmittype reliable, struct sockaddr_in *sin);
    
    static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */
    
    static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context);
    static void clear_sip_domains(void);
    
    static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno);
    
    static int clear_realm_authentication(struct sip_auth *authlist);	/* Clear realm authentication list (at reload) */
    
    static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm);
    
    static void check_rtp_timeout(struct sip_pvt *dialog, time_t t);
    
    static int sip_do_reload(enum channelreloadreason reason);
    
    static int reload_config(enum channelreloadreason reason);
    
    static int expire_register(const void *data);
    
    static void *do_monitor(void *data);
    
    static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
    
    static struct ast_variable *copy_vars(struct ast_variable *src);
    
    /* static int sip_addrcmp(char *name, struct sockaddr_in *sin);	Support for peer matching */
    
    static int sip_refer_allocate(struct sip_pvt *p);
    static void ast_quiet_chan(struct ast_channel *chan);
    static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
    
    static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context);
    
    /*!
     * \brief generic function for determining if a correct transport is being 
     * used to contact a peer
     *
     * this is done as a macro so that the "tmpl" var can be passed either a 
     * sip_request or a sip_peer 
     */
    #define check_request_transport(peer, tmpl) ({ \
    	int ret = 0; \
    	if (peer->socket.type == tmpl->socket.type) \
    		; \
    	else if (!(peer->transports & tmpl->socket.type)) {\
    		ast_log(LOG_ERROR, \
    			"'%s' is not a valid transport for '%s'. we only use '%s'! ending call.\n", \
    
    			get_transport(tmpl->socket.type), peer->name, get_transport_list(peer->transports) \
    
    			); \
    		ret = 1; \
    	} else if (peer->socket.type & SIP_TRANSPORT_TLS) { \
    		ast_log(LOG_WARNING, \
    			"peer '%s' HAS NOT USED (OR SWITCHED TO) TLS in favor of '%s' (but this was allowed in sip.conf)!\n", \
    			peer->name, get_transport(tmpl->socket.type) \
    		); \
    	} else { \
    		ast_debug(1, \
    			"peer '%s' has contacted us over %s even though we prefer %s.\n", \
    			peer->name, get_transport(tmpl->socket.type), get_transport(peer->socket.type) \
    		); \
    	}\
    	(ret); \
    })
    
    
    /*--- Device monitoring and Device/extension state/event handling */
    
    static int cb_extensionstate(char *context, char* exten, int state, void *data);
    static int sip_devicestate(void *data);
    
    static int sip_poke_noanswer(const void *data);
    
    static int sip_poke_peer(struct sip_peer *peer, int force);
    
    static void sip_poke_all_peers(void);
    
    static void sip_peer_hold(struct sip_pvt *p, int hold);
    
    static void mwi_event_cb(const struct ast_event *, void *);
    
    
    /*--- Applications, functions, CLI and manager command helpers */
    
    static const char *sip_nat_mode(const struct sip_pvt *p);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *transfermode2str(enum transfermodes mode) attribute_const;
    
    static int peer_status(struct sip_peer *peer, char *status, int statuslen);
    
    static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Jason Parker's avatar
    Jason Parker committed
    static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
    static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static void  print_group(int fd, ast_group_t group, int crlf);
    
    static const char *dtmfmode2str(int mode) attribute_const;
    
    static int str2dtmfmode(const char *str) attribute_unused;
    
    static const char *insecure2str(int mode) attribute_const;
    
    static void cleanup_stale_contexts(char *new, char *old);
    static void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
    static const char *domain_mode_to_text(const enum domain_mode mode);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_domains(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
    static char *sip_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *_sip_qualify_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]);
    static char *sip_qualify_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_show_mwi(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static const char *subscription_type2str(enum subscriptiontype subtype) attribute_pure;
    
    static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
    static char *complete_sip_peer(const char *word, int state, int flags2);
    
    static char *complete_sip_registered_peer(const char *word, int state, int flags2);
    
    static char *complete_sip_show_history(const char *line, const char *word, int pos, int state);
    
    static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state);
    
    static char *complete_sip_unregister(const char *line, const char *word, int pos, int state);
    
    static char *complete_sipnotify(const char *line, const char *word, int pos, int state);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_do_debug_ip(int fd, const char *arg);
    static char *sip_do_debug_peer(int fd, const char *arg);
    
    static char *sip_do_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static int sip_dtmfmode(struct ast_channel *chan, const char *data);
    static int sip_addheader(struct ast_channel *chan, const char *data);
    
    static int sip_do_reload(enum channelreloadreason reason);
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
    
    /*--- Debugging 
    	Functions for enabling debug per IP or fully, or enabling history logging for
    	a SIP dialog
    */
    
    static void sip_dump_history(struct sip_pvt *dialog);	/* Dump history to debuglog at end of dialog, before destroying data */
    
    static inline int sip_debug_test_addr(const struct sockaddr_in *addr);
    static inline int sip_debug_test_pvt(struct sip_pvt *p);
    
    
    
    /*! \brief Append to SIP dialog history 
    	\return Always returns 0 */
    #define append_history(p, event, fmt , args... )	append_history_full(p, "%-15s " fmt, event, ## args)
    
    static void append_history_full(struct sip_pvt *p, const char *fmt, ...);
    
    static void sip_dump_history(struct sip_pvt *dialog);
    
    static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime);
    
    static int update_call_counter(struct sip_pvt *fup, int event);
    static void sip_destroy_peer(struct sip_peer *peer);
    
    static void sip_destroy_peer_fn(void *peer);
    
    static void set_peer_defaults(struct sip_peer *peer);
    static struct sip_peer *temp_peer(const char *name);
    static void register_peer_exten(struct sip_peer *peer, int onoff);
    
    static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int forcenamematch, int devstate_only);
    
    static int sip_poke_peer_s(const void *data);
    
    static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req);
    static void reg_source_db(struct sip_peer *peer);
    static void destroy_association(struct sip_peer *peer);
    
    static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno);
    
    static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v);
    
    David Vossel's avatar
    David Vossel committed
    static void set_socket_transport(struct sip_socket *socket, int transport);
    
    static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, const char *useragent, int expirey, int deprecated_username, int lastms);
    
    static void update_peer(struct sip_peer *p, int expire);
    
    static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
    static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername);
    
    static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in *sin, int devstate_only);
    
    static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
    
    
    /*--- Internal UA client handling (outbound registrations) */
    
    David Vossel's avatar
    David Vossel committed
    static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us, struct sip_pvt *p);
    
    static void sip_registry_destroy(struct sip_registry *reg);
    
    static int sip_register(const char *value, int lineno);
    
    static const char *regstate2str(enum sipregistrystate regstate) attribute_const;
    
    static int sip_reregister(const void *data);
    
    static int __sip_do_register(struct sip_registry *r);
    
    static int sip_reg_timeout(const void *data);
    
    static void sip_send_all_registers(void);
    
    static int sip_reinvite_retry(const void *data);
    
    static void append_date(struct sip_request *req);	/* Append date to SIP packet */
    
    static int determine_firstline_parts(struct sip_request *req);
    
    static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
    
    static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize);
    
    static int find_sip_method(const char *msg);
    static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported);
    
    static unsigned int parse_allowed_methods(struct sip_request *req);
    static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_request *req);
    
    static int parse_request(struct sip_request *req);
    
    static const char *get_header(const struct sip_request *req, const char *name);
    
    static const char *referstatus2str(enum referstatus rstatus) attribute_pure;
    
    static int method_match(enum sipmethod id, const char *name);
    
    static void parse_copy(struct sip_request *dst, const struct sip_request *src);
    
    static char *get_in_brackets(char *tmp);
    static const char *find_alias(const char *name, const char *_default);
    static const char *__get_header(const struct sip_request *req, const char *name, int *start);
    static int lws2sws(char *msgbuf, int len);
    static void extract_uri(struct sip_pvt *p, struct sip_request *req);
    
    static char *remove_uri_parameters(char *uri);
    
    static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req);
    static int get_also_info(struct sip_pvt *p, struct sip_request *oreq);
    static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
    static int set_address_from_contact(struct sip_pvt *pvt);
    static void check_via(struct sip_pvt *p, struct sip_request *req);
    static char *get_calleridname(const char *input, char *output, size_t outputsize);
    
    static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
    static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
    
    static int get_destination(struct sip_pvt *p, struct sip_request *oreq);
    
    static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline);
    
    static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
    
    static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
    static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
    static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req, struct ast_party_redirecting *redirecting, int set_call_forward);
    
    static int get_domain(const char *str, char *domain, int len); 
    static void get_realm(struct sip_pvt *p, const struct sip_request *req);
    
    /*-- TCP connection handling ---*/
    
    static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session);
    
    static void *sip_tcp_worker_fn(void *);
    
    
    /*--- Constructing requests and responses */
    static void initialize_initreq(struct sip_pvt *p, struct sip_request *req);
    static int init_req(struct sip_request *req, int sipmethod, const char *recip);
    static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch);
    
    static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod);
    
    static int init_resp(struct sip_request *resp, const char *msg);
    
    static inline int resp_needs_contact(const char *msg, enum sipmethod method);
    
    static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req);
    
    static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p);
    static void build_via(struct sip_pvt *p);
    static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer);
    
    static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin, int newdialog);
    
    static char *generate_random_string(char *buf, size_t size);
    static void build_callid_pvt(struct sip_pvt *pvt);
    static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain);
    static void make_our_tag(char *tagbuf, size_t len);
    static int add_header(struct sip_request *req, const char *var, const char *value);
    static int add_header_contentLength(struct sip_request *req, int len);
    static int add_line(struct sip_request *req, const char *line);
    static int add_text(struct sip_request *req, const char *text);
    
    static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode);
    
    static int add_rpid(struct sip_request *req, struct sip_pvt *p);
    
    static int add_vidupdate(struct sip_request *req);
    static void add_route(struct sip_request *req, struct sip_route *route);
    
    static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
    
    static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field);
    static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field);
    
    static void set_destination(struct sip_pvt *p, char *uri);
    static void append_date(struct sip_request *req);
    static void build_contact(struct sip_pvt *p);
    
    /*------Request handling functions */
    
    static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock);
    
    static int handle_request_update(struct sip_pvt *p, struct sip_request *req);
    
    static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, const char *e, int *nounlock);
    
    static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, int *nounlock);
    
    static int handle_request_bye(struct sip_pvt *p, struct sip_request *req);
    
    static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, const char *e);
    
    static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req);
    static int handle_request_message(struct sip_pvt *p, struct sip_request *req);
    
    static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e);
    
    static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
    static int handle_request_options(struct sip_pvt *p, struct sip_request *req);
    
    static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin);
    
    static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e);
    
    static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno);
    
    
    /*------Response handling functions */
    
    static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
    
    /*------ T38 Support --------- */
    static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
    static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan);
    static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl);
    
    static void change_t38_state(struct sip_pvt *p, int state);
    
    /*------ Session-Timers functions --------- */
    static void proc_422_rsp(struct sip_pvt *p, struct sip_request *rsp);
    static int  proc_session_timer(const void *vp);
    static void stop_session_timer(struct sip_pvt *p);
    static void start_session_timer(struct sip_pvt *p);
    static void restart_session_timer(struct sip_pvt *p);
    static const char *strefresher2str(enum st_refresher r);
    static int parse_session_expires(const char *p_hdrval, int *const p_interval, enum st_refresher *const p_ref);
    static int parse_minse(const char *p_hdrval, int *const p_interval);
    static int st_get_se(struct sip_pvt *, int max);
    static enum st_refresher st_get_refresher(struct sip_pvt *);
    static enum st_mode st_get_mode(struct sip_pvt *);
    static struct sip_st_dlg* sip_st_alloc(struct sip_pvt *const p);
    
    
    /*------- RTP Glue functions -------- */
    static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *instance, struct ast_rtp_instance *vinstance, struct ast_rtp_instance *tinstance, int codecs, int nat_active);
    
    
    /*!--- SIP MWI Subscription support */
    static int sip_subscribe_mwi(const char *value, int lineno);
    static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi);
    static void sip_send_all_mwi_subscriptions(void);
    static int sip_subscribe_mwi_do(const void *data);
    static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi);
    
    /*! \brief Definition of this channel for PBX channel registration */
    
    static const struct ast_channel_tech sip_tech = {
    
    	.description = "Session Initiation Protocol (SIP)",
    
    	.capabilities = AST_FORMAT_AUDIO_MASK,	/* all audio formats */
    
    	.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
    
    	.requester = sip_request_call,			/* called with chan unlocked */
    	.devicestate = sip_devicestate,			/* called with chan unlocked (not chan-specific) */
    	.call = sip_call,			/* called with chan locked */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	.send_html = sip_sendhtml,
    
    	.hangup = sip_hangup,			/* called with chan locked */
    	.answer = sip_answer,			/* called with chan locked */
    	.read = sip_read,			/* called with chan locked */
    	.write = sip_write,			/* called with chan locked */
    	.write_video = sip_write,		/* called with chan locked */
    
    	.write_text = sip_write,
    
    	.indicate = sip_indicate,		/* called with chan locked */
    	.transfer = sip_transfer,		/* called with chan locked */
    	.fixup = sip_fixup,			/* called with chan locked */
    	.send_digit_begin = sip_senddigit_begin,	/* called with chan unlocked */
    
    	.bridge = ast_rtp_instance_bridge,			/* XXX chan unlocked ? */
    	.early_bridge = ast_rtp_instance_early_bridge,
    
    	.send_text = sip_sendtext,		/* called with chan locked */
    
    	.func_channel_read = acf_channel_read,
    
    	.get_pvt_uniqueid = sip_get_callid,
    
    /*! \brief This version of the sip channel tech has no send_digit_begin
    
     * callback so that the core knows that the channel does not want
     * DTMF BEGIN frames.
     * The struct is initialized just before registering the channel driver,
     * and is for use with channels using SIP INFO DTMF.
     */
    static struct ast_channel_tech sip_tech_info;
    
    /*! \brief Working TLS connection configuration */
    
    static struct ast_tls_config sip_tls_cfg;
    
    
    /*! \brief Default TLS connection configuration */
    
    static struct ast_tls_config default_tls_cfg;
    
    
    /*! \brief The TCP server definition */
    
    static struct ast_tcptls_session_args sip_tcp_desc = {
    
    	.accept_fd = -1,
    	.master = AST_PTHREADT_NULL,
    	.tls_cfg = NULL,
    	.poll_timeout = -1,
    
    	.accept_fn = ast_tcptls_server_root,
    
    	.worker_fn = sip_tcp_worker_fn,
    };
    
    
    /*! \brief The TCP/TLS server definition */
    
    static struct ast_tcptls_session_args sip_tls_desc = {
    
    	.accept_fd = -1,
    	.master = AST_PTHREADT_NULL,
    	.tls_cfg = &sip_tls_cfg,
    	.poll_timeout = -1,
    
    	.accept_fn = ast_tcptls_server_root,
    
    	.worker_fn = sip_tcp_worker_fn,
    };
    
    
    /* wrapper macro to tell whether t points to one of the sip_tech descriptors */
    #define IS_SIP_TECH(t)  ((t) == &sip_tech || (t) == &sip_tech_info)
    
    
    Jason Parker's avatar
    Jason Parker committed
    /*! \brief map from an integer value to a string.
    
     * If no match is found, return errorstring
     */
    static const char *map_x_s(const struct _map_x_s *table, int x, const char *errorstring)
    {
    	const struct _map_x_s *cur;
    
    	for (cur = table; cur->s; cur++)
    		if (cur->x == x)
    			return cur->s;
    	return errorstring;
    }
    
    
    Jason Parker's avatar
    Jason Parker committed
    /*! \brief map from a string to an integer value, case insensitive.
    
     * If no match is found, return errorvalue.
     */
    static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
    {
    	const struct _map_x_s *cur;
    
    	for (cur = table; cur->s; cur++)
    		if (!strcasecmp(cur->s, s))
    			return cur->x;
    	return errorvalue;
    }
    
    
    /*!
     * duplicate a list of channel variables, \return the copy.
     */
    static struct ast_variable *copy_vars(struct ast_variable *src)
    {
    	struct ast_variable *res = NULL, *tmp, *v = NULL;
    
    	for (v = src ; v ; v = v->next) {
    		if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
    			tmp->next = res;
    			res = tmp;
    		}
    	}
    	return res;
    }
    
    /*! \brief SIP TCP connection handler */
    
    static void *sip_tcp_worker_fn(void *data)
    {
    
    	struct ast_tcptls_session_instance *tcptls_session = data;
    
    	return _sip_tcp_helper_thread(NULL, tcptls_session);
    
    /*! \brief SIP TCP thread management function 
    	This function reads from the socket, parses the packet into a request
    */
    
    static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session) 
    
    {
    	int res, cl;
    	struct sip_request req = { 0, } , reqcpy = { 0, };
    	struct sip_threadinfo *me;
    
    
    	me = ast_calloc(1, sizeof(*me));
    
    	if (!me)
    		goto cleanup2;
    
    	me->threadid = pthread_self();
    
    	me->tcptls_session = tcptls_session;
    	if (tcptls_session->ssl)
    
    		me->type = SIP_TRANSPORT_TLS;
    	else
    		me->type = SIP_TRANSPORT_TCP;
    
    
    	ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
    
    	AST_LIST_LOCK(&threadl);
    	AST_LIST_INSERT_TAIL(&threadl, me, list);
    	AST_LIST_UNLOCK(&threadl);
    
    
    	if (!(req.data = ast_str_create(SIP_MIN_PACKET)))
    		goto cleanup;
    	if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET)))
    		goto cleanup;
    
    		struct ast_str *str_save;
    
    		str_save = req.data;
    		memset(&req, 0, sizeof(req));
    		req.data = str_save;
    
    		ast_str_reset(req.data);
    
    
    		str_save = reqcpy.data;
    		memset(&reqcpy, 0, sizeof(reqcpy));
    		reqcpy.data = str_save;
    
    		ast_str_reset(reqcpy.data);
    
    David Vossel's avatar
    David Vossel committed
    			set_socket_transport(&req.socket, SIP_TRANSPORT_TLS);
    
    			req.socket.port = htons(ourport_tls);
    		} else {
    
    David Vossel's avatar
    David Vossel committed
    			set_socket_transport(&req.socket, SIP_TRANSPORT_TCP);
    
    			req.socket.port = htons(ourport_tcp);
    		}
    
    David Vossel's avatar
    David Vossel committed
    		req.socket.fd = tcptls_session->fd;
    
    		res = ast_wait_for_input(tcptls_session->fd, -1);
    
    			ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
    
    			goto cleanup;
    		}
    
    		/* Read in headers one line at a time */
    
    		while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
    
    			ast_mutex_lock(&tcptls_session->lock);
    			if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
    				ast_mutex_unlock(&tcptls_session->lock);
    
    			if (me->stop) 
    				 goto cleanup;
    
    			ast_str_append(&req.data, 0, "%s", buf);
    			req.len = req.data->used;
    
    		copy_request(&reqcpy, &req);
    		parse_request(&reqcpy);
    
    		/* In order to know how much to read, we need the content-length header */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (sscanf(get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
    
    			while (cl > 0) {
    
    				ast_mutex_lock(&tcptls_session->lock);
    				if (!fread(buf, (cl < sizeof(buf)) ? cl : sizeof(buf), 1, tcptls_session->f)) {
    					ast_mutex_unlock(&tcptls_session->lock);
    
    				if (me->stop)
    					goto cleanup;
    				cl -= strlen(buf);
    
    				ast_str_append(&req.data, 0, "%s", buf);
    				req.len = req.data->used;
    
    		/*! \todo XXX If there's no Content-Length or if the content-length and what
    
    				we receive is not the same - we should generate an error */
    
    
    		req.socket.tcptls_session = tcptls_session;
    		handle_request_do(&req, &tcptls_session->remote_address);
    
    	}
    
    cleanup:
    	AST_LIST_LOCK(&threadl);
    	AST_LIST_REMOVE(&threadl, me, list);
    	AST_LIST_UNLOCK(&threadl);
    	ast_free(me);
    cleanup2:
    
    	fclose(tcptls_session->f);
    	tcptls_session->f = NULL;
    	tcptls_session->fd = -1;
    
    	ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
    
    	ao2_ref(tcptls_session, -1);
    	tcptls_session = NULL;
    
    /*!
     * helper functions to unreference various types of objects.
     * By handling them this way, we don't have to declare the
     * destructor on each call, which removes the chance of errors.
     */
    
    static void *unref_peer(struct sip_peer *peer, char *tag)
    
    	ao2_t_ref(peer, -1, tag);
    	return NULL;
    
    static struct sip_peer *ref_peer(struct sip_peer *peer, char *tag)
    {
    
    	ao2_t_ref(peer, 1, tag);
    
    /*! \brief maintain proper refcounts for a sip_pvt's outboundproxy