[asterisk-commits] mmichelson: branch group/pimp_my_sip r378716 - in /team/group/pimp_my_sip: in...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jan 9 10:21:30 CST 2013


Author: mmichelson
Date: Wed Jan  9 10:21:26 2013
New Revision: 378716

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=378716
Log:
Add work previously done in res_sip branch.

This includes some basic service registration, authenticator API, and endpoint identifier API.


Added:
    team/group/pimp_my_sip/include/asterisk/res_sip.h   (with props)
    team/group/pimp_my_sip/res/res_sip.c   (with props)

Added: team/group/pimp_my_sip/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/include/asterisk/res_sip.h?view=auto&rev=378716
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip.h (added)
+++ team/group/pimp_my_sip/include/asterisk/res_sip.h Wed Jan  9 10:21:26 2013
@@ -1,0 +1,410 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson at 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.
+ */
+
+#ifndef _RES_SIP_H
+#define _RES_SIP_H
+
+
+/* Forward declarations of PJSIP stuff */
+struct pjsip_rx_data;
+struct pjsip_module;
+struct pjsip_tx_data;
+struct pjsip_dialog;
+
+/*!
+ * \brief Opaque structure representing related SIP tasks
+ */
+struct ast_sip_work;
+ 
+/*!
+ * \brief Data used for creating authentication challenges.
+ * 
+ * This data gets populated by an authenticator's get_authentication_credentials() callback.
+ */
+struct ast_sip_digest_challenge_data {
+    /*!
+     * The realm to which the user is authenticating. An authenticator MUST fill this in.
+     */
+    const char *realm;
+    /*!
+     * Indicates whether the username and password are in plaintext or encoded as MD5.
+     * If this is non-zero, then the data is an MD5 sum. Otherwise, the username and password are plaintext.
+     * Authenticators MUST set this.
+     */
+    int is_md5;
+    /*!
+     * This is the actual username and secret. The is_md5 field is used to determine which member
+     * of the union is to be used when creating the authentication challenge. In other words, if
+     * is_md5 is non-zero, then we will use the md5 field of the auth union. Otherwise, we will
+     * use the plain struct in the auth union.
+     * Authenticators MUST fill in the appropriate field of the union.
+     */
+    union {
+        /*!
+         * Structure containing the username and password to encode in a digest authentication challenge.
+         */
+        struct {
+            const char *username;
+            const char *password;
+        } plain;
+        /*!
+         * An MD5-encoded string that incorporates the username and password.
+         */
+        const char *md5;
+    } auth;
+    /*!
+     * Domain for which the authentication challenge is being sent. This corresponds to the "domain=" portion of
+     * a digest authentication.
+     *
+     * Authenticators do not have to fill in this field since it is an optional part of a digest.
+     */
+    const char *domain;
+    /*!
+     * Opaque string for digest challenge. This corresponds to the "opaque=" portion of a digest authentication.
+     * Authenticators do not have to fill in this field. If an authenticator does not fill it in, Asterisk will provide one.
+     */
+    const char *opaque;
+    /*!
+     * Nonce string for digest challenge. This corresponds to the "nonce=" portion of a digest authentication.
+     * Authenticators do not have to fill in this field. If an authenticator does not fill it in, Asterisk will provide one.
+     */
+    const char *nonce;
+};
+ 
+/*!
+ * \brief An interchangeable way of handling digest authentication for SIP.
+ * 
+ * An authenticator is responsible for filling in the callbacks provided below. Each is called from a publicly available
+ * function in res_sip. The authenticator can use configuration or other local policy to determine whether authentication
+ * should take place and what credentials should be used when challenging and authenticating a request.
+ */
+struct ast_sip_authenticator {
+    /*! 
+     * \brief Check if a request requires authentication
+     * See ast_sip_requires_authentication for more details
+     */
+    int (*requires_authentication)(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+    /*!
+     * \brief Attempt to authenticate the incoming request
+     * See ast_sip_authenticate_request for more details
+     */
+    int (*authenticate_request)(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+    /*!
+     * \brief Get digest authentication details
+     * See ast_sip_get_authentication_credentials for more details
+    */
+    int (*get_authentication_credentials)(struct ast_sip_endpoint *endpoint, struct sip_digest_challenge_data *challenge);
+};
+ 
+/*!
+ * \brief An entity responsible for identifying the source of a SIP message
+ */
+struct ast_sip_endpoint_identifier {
+    /*!
+     * \brief Callback used to identify the source of a message.
+     * See ast_sip_identify_endpoint for more details
+     */
+    struct ast_sip_endpoint *(*identify_endpoint)(struct pjsip_rx_data *rdata);
+};
+
+/*!
+ * \brief Register a SIP service in Asterisk.
+ *
+ * This is more-or-less a wrapper around pjsip_endpt_register_module().
+ * Registering a service makes it so that PJSIP will call into the
+ * service at appropriate times. For more information about PJSIP module
+ * callbacks, see the PJSIP documentation. Asterisk modules that call
+ * this function will likely do so at module load time.
+ *
+ * \param module The module that is to be registered with PJSIP
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_register_service(pjsip_module *module);
+ 
+/*!
+ * This is the opposite of ast_sip_register_service().  Unregistering a
+ * service means that PJSIP will no longer call into the module any more.
+ * This will likely occur when an Asterisk module is unloaded.
+ *
+ * \param module The PJSIP module to unregister
+ */
+void ast_sip_unregister_service(pjsip_module *module);
+ 
+/*!
+ * \brief Register a SIP authenticator
+ *
+ * An authenticator has three main purposes:
+ * 1) Determining if authentication should be performed on an incoming request
+ * 2) Gathering credentials necessary for issuing an authentication challenge
+ * 3) Authenticating a request that has credentials
+ *
+ * Asterisk provides a default authenticator, but it may be replaced by a
+ * custom one if desired.
+ *
+ * \param auth The authenticator to register
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_register_authenticator(struct ast_sip_authenticator *auth);
+ 
+/*!
+ * \brief Unregister a SIP authenticator
+ *
+ * When there is no authenticator registered, requests cannot be challenged
+ * or authenticated.
+ *
+ * \param auth The authenticator to unregister
+ */
+void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth);
+ 
+/*!
+ * \brief Register a SIP endpoint identifier
+ *
+ * An endpoint identifier's purpose is to determine which endpoint a given SIP
+ * message has come from.
+ *
+ * Multiple endpoint identifiers may be registered so that if an endpoint
+ * cannot be identified by one identifier, it may be identified by another.
+ *
+ * Asterisk provides two endpoint identifiers. One identifies endpoints based
+ * on the user part of the From header URI. The other identifies endpoints based
+ * on the source IP address.
+ *
+ * If the order in which endpoint identifiers is run is important to you, then
+ * be sure to load individual endpoint identifier modules in the order you wish
+ * for them to be run in modules.conf
+ *
+ * \param identifier The SIP endpoint identifier to register
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier);
+ 
+/*!
+ * \brief Unregister a SIP endpoint identifier
+ *
+ * This stops an endpoint identifier from being used.
+ *
+ * \param identifier The SIP endoint identifier to unregister
+ */
+void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier);
+
+/*!
+ * \brief Create a new SIP work structure
+ *
+ * A SIP work is a means of grouping together SIP tasks. For instance, one
+ * might create a SIP work so that all tasks for a given SIP dialog will
+ * be grouped together. Grouping the work together ensures that the
+ * threadpool will distribute the tasks in such a way so that grouped work
+ * will execute sequentially. Executing grouped tasks sequentially means
+ * less contention for shared resources.
+ *
+ * \retval NULL Failure
+ * \retval non-NULL Newly-created SIP work
+ */
+struct ast_sip_work *ast_sip_create_work(void);
+ 
+/*!
+ * \brief Destroy a SIP work structure
+ *
+ * \param work The SIP work to destroy
+ */
+void ast_sip_destroy_work(struct ast_sip_work *work);
+ 
+/*!
+ * \brief Pushes a task into the SIP threadpool
+ *
+ * This uses the SIP work provided to determine how to push the task.
+ *
+ * \param work The SIP work to which the task belongs
+ * \param sip_task The task to execute
+ * \param task_data The parameter to pass to the task when it executes
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_push_task(struct ast_sip_work *work, int (*sip_task)(void *), void *task_data);
+
+/*!
+ * \brief General purpose method for sending a SIP request
+ *
+ * Its typical use would be to send one-off messages such as an out of dialog
+ * SIP MESSAGE.
+ *
+ * \param method The method of the SIP request to send
+ * \param body The message body for the SIP request
+ * \dlg Optional. If specified, the dialog on which to send the message. Otherwise, the message
+ *      will be sent out of dialog.
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_send_request(const char *method, const char *body, struct pjsip_dialog *dlg);
+ 
+/*!
+ * \brief Determine if an incoming request requires authentication
+ *
+ * This calls into the registered authenticator's requires_authentication callback
+ * in order to determine if the request requires authentication.
+ *
+ * If there is no registered authenticator, then authentication will be assumed
+ * not to be required.
+ *
+ * \param endpoint The endpoint from which the request originates
+ * \param rdata The incoming SIP request
+ * \retval non-zero The request requires authentication
+ * \retval 0 The request does not require authentication
+ */
+int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+ 
+/*!
+ * \brief Authenticate an inbound SIP request
+ *
+ * This calls into the registered authenticator's authenticate_request callback
+ * in order to determine if the request contains proper credentials as to be
+ * authenticated.
+ *
+ * If there is no registered authenticator, then the request will assumed to be
+ * authenticated properly.
+ *
+ * \param endpoint The endpoint from which the request originates
+ * \param rdata The incoming SIP request
+ * \retval 0 Successfully authenticated
+ * \retval nonzero Failure to authenticate
+ */
+int ast_sip_authenticate_request(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+ 
+/*!
+ * \brief Get authentication credentials in order to challenge a request
+ *
+ * This calls into the registered authenticator's get_authentication_credentials
+ * callback in order to get credentials required for challenging a request.
+ *
+ * \param endpoint The endpoint whose credentials are being gathered
+ * \param[out] challenge The necessary data in order to be able to challenge a request
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_get_authentication_credentials(struct ast_sip_endpoint *endpoint, struct ast_sip_digest_challenge_data *challenge);
+ 
+/*!
+ * \brief Possible returns from ast_sip_check_authentication
+ */
+enum ast_sip_check_auth_result {
+    /*! Authentication challenge sent */
+    AST_SIP_AUTHENTICATION_CHALLENGE_SENT,
+    /*! Authentication succeeded */
+    AST_SIP_AUTHENTICATION_SUCCESS,
+    /*! Authentication failed */
+    AST_SIP_AUTHENTICATION_FAILED,
+    /*! Authentication not required */
+    AST_SIP_AUTHENTICATION_NOT_REQUIRED,
+};
+ 
+/*!
+ * \brief Shortcut routine to check for authentication of an incoming request
+ *
+ * This is a wrapper that will call into a registered authenticator to see if a request
+ * should be authenticated. Then if it should be, will attempt to authenticate. If the
+ * request cannot be authenticated, then a challenge will be sent. Calling this can be
+ * a suitable substitute for calling ast_sip_requires_authentication(),
+ * ast_sip_authenticate_request(), and ast_sip_get_authentication_credentials()
+ *
+ * \param endpoint The endpoint from the request was sent
+ * \param rdata The request to potentially authenticate
+ * \return The result of checking authentication.
+ */
+ast_sip_check_authentication(struct ast_sip_endpoint *endpoint, struct pjsip_rxdata *rdata);
+ 
+/*!
+ * \brief Challenge an inbound SIP request with a 401
+ *
+ *
+ * This method will send an authentication challenge based on the details
+ * given in its parameter
+ *
+ * \param challenge Details to help in constructing a WWW-Authenticate header
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_challenge_request(struct sip_digest_challenge_data *challenge);
+ 
+/*!
+ * \brief Determine the endpoint that has sent a SIP message
+ *
+ * This will call into each of the registered endpoint identifiers'
+ * identify_endpoint() callbacks until one returns a non-NULL endpoint.
+ * This will return an ao2 object. Its reference count will need to be
+ * decremented when completed using the endpoint.
+ *
+ * \param rdata The inbound SIP message to use when identifying the endpoint.
+ * \retval NULL No matching endpoint
+ * \retval non-NULL The matching endpoint
+ */
+struct ast_sip_endpoint *ast_sip_identify_endpoint(struct pjsip_rx_data *rdata);
+ 
+/*!
+ * \brief Add a header to an outbound SIP message
+ *
+ * \param tdata The message to add the header to
+ * \param name The header name
+ * \param value The header value
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_add_header(struct pjsip_tx_data *tdata, const char *name, const char *value);
+ 
+/*!
+ * \brief Add a body to an outbound SIP message
+ *
+ * If this is called multiple times, the latest body will replace the current
+ * body.
+ *
+ * \param tdata The message to add the body to
+ * \param body The message body to add
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_add_body(struct pjsip_tx_data *tdata, const char *body);
+ 
+/*!
+ * \brief Add a multipart body to an outbound SIP message
+ *
+ * This will treat each part of the input array as part of a multipart body and
+ * add each part to the SIP message.
+ *
+ * \param tdata The message to add the body to
+ * \param bodies The parts of the body to add
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_add_body(struct pjsip_tx_data *tdata, const char *bodies[]);
+ 
+/*!
+ * \brief Append body data to a SIP message
+ *
+ * This acts mostly the same as ast_sip_add_body, except that rather than replacing
+ * a body if it currently exists, it appends data to an existing body.
+ *
+ * \param tdata The message to append the body to
+ * \param body The string to append to the end of the current body
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_append_body(struct pjsip_tx_data *tdata, const char *body);
+
+#endif /* _RES_SIP_H */

Propchange: team/group/pimp_my_sip/include/asterisk/res_sip.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/pimp_my_sip/include/asterisk/res_sip.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/pimp_my_sip/include/asterisk/res_sip.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/group/pimp_my_sip/res/res_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip.c?view=auto&rev=378716
==============================================================================
--- team/group/pimp_my_sip/res/res_sip.c (added)
+++ team/group/pimp_my_sip/res/res_sip.c Wed Jan  9 10:21:26 2013
@@ -1,0 +1,161 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson at 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.
+ */
+
+#include "asterisk.h"
+#include "pjsip.h"
+#include "pjlib.h"
+
+#include "asterisk/res_sip.h"
+
+static pjsip_endpoint *ast_pjsip_endpoint;
+
+int ast_sip_register_service(pjsip_module *module)
+{
+	if (!ast_pjsip_endpoint) {
+		ast_log(LOG_ERROR, "There is no PJSIP endpoint. Unable to register services\n");
+		return -1;
+	}
+	if (pjsip_endpt_register_module(ast_pjsip_endpoint, module) != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Unable to register module %.*s\n", pj_strlen(module->name), pj_strbuf(module->name));
+		return -1;
+	}
+	ast_debug(1, "Registered SIP service %.*s\n", pj_strlen(module->name), pj_strbuf(module->name));
+	return 0;
+}
+
+void ast_sip_unregister_service(pjsip_module *module)
+{
+	if (!ast_pjsip_endpoint) {
+		return;
+	}
+	pjsip_endpt_unregister_module(ast_pjsip_endpoint, module);
+	ast_debug(1, "Unregistered SIP service %.*s\n", pj_strlen(module->name), pj_strbuf(module->name));
+}
+
+AO2_GLOBAL_OBJ_STATIC(registered_authenticator);
+
+int ast_sip_register_authenticator(struct ast_sip_authenticator *auth)
+{
+	RAII_VAR(struct ast_sip_authenticator *, reg, ao2_global_obj_ref(registered_authenticator), ao2_cleanup);
+	if (reg) {
+		ast_log(LOG_WARNING, "Authenticator %p is already registered. Cannot register a new one\n", reg);
+		return -1;
+	}
+	ao2_global_obj_replace_unref(registered_authenticator, auth);
+	ast_debug(1, "Registered SIP authenticator module %p\n", auth);
+	return 0;
+}
+
+void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth)
+{
+	RAII_VAR(struct ast_sip_authenticator *, reg, ao2_global_obj_ref(registered_authenticator), ao2_cleanup);
+	if (auth != reg) {
+		ast_log(LOG_WARNING, "Trying to unregister authenticator %p but authenticator %p registered\n",
+				auth, reg);
+		return;
+	}
+	ao2_global_obj_release(registered_authenticator);
+	ast_debug(1, "Unregistered SIP authenticator %p\n", auth);
+}
+
+int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata)
+{
+	RAII_VAR(struct ast_sip_authenticator *, reg, ao2_global_obj_ref(registered_authenticator), ao2_cleanup);
+	if (!reg) {
+		ast_log(LOG_WARNING, "No SIP authenticator registered. Assuming authentication is not required\n");
+		return 0;
+	}
+
+	return reg->requires_authentication(endpoint, rdata);
+}
+
+int ast_sip_authenticate_request(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata)
+{
+	RAII_VAR(struct ast_sip_authenticator *, reg, ao2_global_obj_ref(registered_authenticator), ao2_cleanup);
+	if (!reg) {
+		ast_log(LOG_WARNING, "No SIP authenticator registered. Assuming request authenticated properly\n");
+		return 0;
+	}
+
+	return reg->authenticate_request(endpoint, rdata);
+}
+
+int ast_sip_get_authentication_credentials(struct ast_sip_endpoint *endpoint,
+		struct sip_digest_challenge_data *challenge)
+{
+	RAII_VAR(struct ast_sip_authenticator *, reg, ao2_global_obj_ref(registered_authenticator), ao2_cleanup);
+	if (!reg) {
+		ast_log(LOG_WARNING, "No SIP authenticator registered. Assuming no authentication credentials\n");
+		return -1;
+	}
+
+	return reg->authenticate_request(endpoint, rdata);
+}
+
+struct endpoint_identifier_list {
+	struct ast_sip_endpoint_identifier *identifier;
+	AST_RWLIST_ENTRY(endpoint_identifier_list) list;
+}
+
+AST_RWLIST_HEAD_STATIC(endpoint_identifiers, endpoint_identifier_list);
+
+int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
+{
+	struct endpoint_identifier_list *id_list_item;
+	SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+	id_list_item = ast_calloc(1, sizeof(*id_list_item));
+	if (!id_list_item) {
+		ast_log(LOG_ERROR, "Unabled to add endpoint identifier. Out of memory.\n");
+		return -1;
+	}
+	id_list_item->identifier = identifier;
+
+	AST_RWLIST_INSERT_TAIL(&endpoint_identifiers, id_list_item, list);
+	ast_debug(1, "Registered endpoint identifier %p\n", identifier);
+}
+
+void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier)
+{
+	struct endpoint_identifier_list *iter;
+	SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&endpoint_identifiers, iter, list) {
+		if (iter->identifier == identifier) {
+			AST_RWLIST_REMOVE_CURRENT(list);
+			ast_free(iter);
+			ast_debug(1, "Unregistered endpoint identifier %p\n", identifier);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+}
+
+struct ast_sip_endpoint *ast_sip_identify_endpoint(struct pjsip_rx_data *rdata)
+{
+	struct endpoint_identifier_list *iter;
+	struct ast_sip_endpoint *endpoint;
+	SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+	AST_RWLIST_TRAVERSE(&endpoint_identifiers, iter, list) {
+		ast_assert(iter->identifier->identify_endpoint != NULL);
+		endpoint = iter->identifier->identify_endpoint(rdata);
+		if (endpoint) {
+			break;
+		}
+	}
+	return endpoint;
+}

Propchange: team/group/pimp_my_sip/res/res_sip.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/pimp_my_sip/res/res_sip.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/pimp_my_sip/res/res_sip.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the asterisk-commits mailing list