· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Asterisk Source/Sip Module

AsteriskSource/SipModule


* asterisk-0.2.0

* you need a username and password which will allow you to call someone who is registered.

* To receive a call, you must be a registered user and have an extension.

* SIP Address of Record (AOR). This is the address people use to call you, the format looks like an email address.

* SIP Contact Address is the temporary address and is determined by what IP address you currently have, the device name, and the port number you are using for SIP. This address is usually temporary and stored in memory. When you register with a SIP server, the server maps this address onto your SIP Address of Record (AOR).

* load_module -> restart_monitor

* restart_monitor -> pthread_create(&monitor_thread, NULL, do_monitor, NULL)

* do_monitor(NULL)
  • for
    • ast_io_wait(io, res) -> callback(sipsock_read)

* int load_module()
  • cfg = ast_load(config);
  • v = ast_variable_browse(cfg, "general");
    • set context, language, bindaddr.sin_addr, bindaddr.sin_port
  • cat = ast_category_browse(cfg, NULL);
    • user = build_user(cat, ast_variable_browse(cfg, cat));
    • peer = build_peer(cat, ast_variable_browse(cfg, cat));
    • friend: can make and accept calls.
    • peer: can only make calls.
    • user: can only accept calls.
  • bindaddr.sin_family = AF_INET;
  • sipsock = socket(AF_INET, SOCK_DGRAM, 0);
  • bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr))
  • ast_channel_register(type, tdesc, capability, sip_request)
  • restart_monitor();
  • return 0;

* static int restart_monitor(void)
  • pthread_create(&monitor_thread, NULL, do_monitor, NULL)
  • return 0;

* static void *do_monitor(void *data)
  • sched = sched_context_create();
struct sched_context {
        /* Number of events processed */
        int eventcnt;

        /* Number of outstanding schedule events */
        int schedcnt;

        /* Schedule entry and main queue */
        struct sched *schedq;

#ifdef SCHED_MAX_CACHE
        /* Cache of unused schedule structures and how many */
        struct sched *schedc;
        int schedccnt;
#endif
};
struct sched_context *sched_context_create(void)
{
        struct sched_context *tmp;
        tmp = malloc(sizeof(struct sched_context));
        if (tmp) {
                tmp->eventcnt = 1;
                tmp->schedcnt = 0;
                tmp->schedq = NULL;
#ifdef SCHED_MAX_CACHE
                tmp->schedc = NULL;
                tmp->schedccnt = 0;
#endif
        }
        return tmp;
}
  • io = io_context_create();
struct io_context {
        /* Poll structure */
        struct pollfd *fds;
        /* Associated I/O records */
        struct io_rec *ior;
        /* First available fd */
        unsigned int fdcnt;
        /* Maximum available fd */
        unsigned int maxfdcnt;
        /* Currently used io callback */
        int current_ioc;
        /* Whether something has been deleted */
        int needshrink;
};
struct io_context *io_context_create(void)
{
        /* Create an I/O context */
        struct io_context *tmp;
        tmp = malloc(sizeof(struct io_context));
        if (tmp) {
                tmp->needshrink = 0;
                tmp->fdcnt = 0;
                tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
                tmp->current_ioc = -1;
                tmp->fds = malloc((GROW_SHRINK_SIZE/2) * sizeof(struct pollfd));                if (!tmp->fds) {
                        free(tmp);
                        tmp = NULL;
                } else {
                        tmp->ior =  malloc((GROW_SHRINK_SIZE/2) * sizeof(struct io_rec));
                        if (!tmp->ior) {
                                free(tmp->fds);
                                free(tmp);
                                tmp = NULL;
                        }
                }
        }
        return tmp;
}
  • ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
    • include/asterisk/io.h
    • #define AST_IO_IN POLLIN
    • <poll.h> POLLIN
int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)

{
        /*
         * Add a new I/O entry for this file descriptor
         * with the given event mask, to call callback with
         * data as an argument.  Returns NULL on failure.
         */
        int *ret;
        DEBUG(ast_log(LOG_DEBUG, "ast_io_add()\n"));
        if (ioc->fdcnt >= ioc->maxfdcnt) {
                /*
                 * We don't have enough space for this entry.  We need to
                 * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
                 */
                if (io_grow(ioc))
                        return NULL;
        }

        /*
         * At this point, we've got sufficiently large arrays going
         * and we can make an entry for it in the pollfd and io_r
         * structures.
         */
        ioc->fds[ioc->fdcnt].fd = fd;
        ioc->fds[ioc->fdcnt].events = events;
        ioc->ior[ioc->fdcnt].callback = callback;
        ioc->ior[ioc->fdcnt].data = data;
        ioc->ior[ioc->fdcnt].id = (int *)malloc(sizeof(int));
        /* Bonk if we couldn't allocate an int */
        if (!ioc->ior[ioc->fdcnt].id)
                return NULL;
        *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
        ret = ioc->ior[ioc->fdcnt].id;
        ioc->fdcnt++;
        return ret;
}
  • for(;;)
    • res = ast_sched_wait(sched);
      • óÀ½¿¡´Â con->schedq °¡ NULL À̹ǷÎ, return -1
      • request °¡ REGISTER ÀÎ °æ¿ì¿¡ ast_sched_add ¿¡ ÀÇÇØ con->schedq °¡ NULL ÀÌ ¾Æ´Ï°Ô µÊ
//! Determines number of seconds until the next outstanding event to take place
/*!
 * \param con context to act upon
 * Determine the number of seconds until the next outstanding event
 * should take place, and return the number of milliseconds until
 * it needs to be run.  This value is perfect for passing to the poll
 * call.  Returns "-1" if there is nothing there are no scheduled events
 * (and thus the poll should not timeout)
 */
    • res = ast_io_wait(io, res);
//! Waits for IO
/*!
 * \param ioc which context to act upon
 * \param howlong how many milliseconds to wait
 * Wait for I/O to happen, returning after
 * howlong milliseconds, and after processing
 * any necessary I/O.  Returns the number of
 * I/O events which took place.
 * If the value of howlong is -1, poll() shall block 
 * until a requested event occurs or until the call is 
 * interrupted.
 */
      • res = poll(ioc->fds, ioc->fdcnt, howlong);
      • ¿©±â¼­ ´ë±âÇϰí ÀÖ½¿.
      • howlong=-1
        • block until a requested event occurs
        • or until the call is interrupted
      • howlong=0
        • poll shall return immediately.
      • howlong > 0
        • ÇØ´ç½Ã°£¸¸Å­ ±â´Ù¸².
      • event °¡ ¹ß»ýÇÏÁö ¾ÊÀ¸¸é, 0 À» return
      • event °¡ ¹ß»ýÇÏ¿´À¸¸é, ÇØ´çµÇ´Â fd °¹¼ö¸¦ return
      • ioc->ior[x].callback (sipsock_read)
    • ast_sched_runq(sched);
//! Runs the queue
/*!
 * \param con Scheduling context to run
 * Run the queue, executing all callbacks which need to be performed
 * at this time.  Returns the number of events processed.
 */

* handle_request ¿¡¼­ request °¡ REGISTER ÀÎ °æ¿ì
  • register_verify
    • parse_contact
      • ast_sched_add

sipsock_read


* static int sipsock_read(int *id, int fd, short events, void *ignore)
  • res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
  • parse(&req);
  • p = find_call(&req, &sin);
    • callid = get_header(req, "Call-ID");
    • °¢ È£(call)¸¦ ±¸ºÐÇÏ´Â À¯ÀÏÇÑ id
    • p = iflist;
    • while(p)
      • if (!strcmp(p->callid, callid)) return p;
    • return sip_alloc(callid, sin);
  • handle_request(p, &req, &sin);
    • get_destination(p)
      • ast_exists_extension(NULL, p->context, c, 1, NULL)
* static void parse(struct sip_request *req)
struct sip_request {
        int len;
        int headers;                                    /* SIP Headers */
        char *header[SIP_MAX_HEADERS];
        int lines;                                              /* SDP Content */
        char *line[SIP_MAX_LINES];
        char data[SIP_MAX_PACKET];
};
  • recvfrom ¿¡¼­ req.data ¿¡ receive ÇÏ¿´À½.
  • header ¿Í line Àº ºóÄ­À¸·Î ±¸º°µÊ
  • data ¿¡ ¹ÞÀº ¹®ÀÚ¿­À» line º°·Î ±¸º°ÇÏ¿© header ¿Í line ¿¡ assign ÇÔ.
* static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin)
  • callid = get_header(req, "Call-ID");
  • °¢ È£(call)¸¦ ±¸ºÐÇÏ´Â À¯ÀÏÇÑ id
  • p = iflist;
  • while(p)
    • if (!strcmp(p->callid, callid))
      • return p;
    • p = p->next;
  • return sip_alloc(callid, sin);

* static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin)
  • p = malloc(sizeof(struct sip_pvt));
  • p->rtp = ast_rtp_new(sched, io);
  • ast_rtp_set_data(p->rtp, p);
  • ast_rtp_set_callback(p->rtp, rtpready);
  • if (!callid)
    • build_callid(p->callid, sizeof(p->callid), p->ourip);
  • else
    • strncpy(p->callid, callid, sizeof(p->callid) - 1);
  • p->next = iflist;
  • iflist = p;
  • return p;

handle_request

* handle_request(p, &req, &sin);
  • struct sip_pvt *p;
    • p = find_call(&req, &sin);
  • struct sip_request req;
    • memset(&req, 0, sizeof(req));
    • res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
  • struct sockaddr_in sin;
    • res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
* recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
  • ¼ö½ÅÇÑ ÀÚ·á´Â buffer ¿¡ ÀúÀåµÇ°í, ¼Û½ÅÀÚÀÇ ÁÖ¼Ò´Â address ¿¡ ÀúÀåµÊ.
  • socket
    • Specifies the socket file descriptor.
  • buffer
    • Points to the buffer where the message should be stored.
  • length
    • Specifies the length in bytes of the buffer pointed to by the buffer argument.
  • flags
  • address
    • A null pointer, or points to a sockaddr structure in which the sending address is to be stored. The length and format of the address depend on the address family of the socket.
  • address_len
    • Specifies the length of the sockaddr structure pointed to by the address argument

* static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin)
  • cseq = get_header(req, "Cseq");
  • °¢ ¿äû¸¶´Ù Áõ°¡ÇÏ´Â °ª
  • cmd = req->header[0];
  • if (!strcasecmp(cmd, "INVITE"))
    • if (!ignore)
      • copy_request(&p->initreq, req);
      • check_user(p, req);
      • check_via(p, req);
      • if (process_sdp(p, req)) return -1;
    • if (!p->lastinvite)
      • if ((res = get_destination(p)))
        • sip_destroy(p);
      • else
        • c = sip_new(p, AST_STATE_RING);
  • else if (!strcasecmp(cmd, "CANCEL") || !strcasecmp(cmd, "BYE"))
    • CANCEL request, BYE request
    • copy_request(&p->initreq, req);
    • if (p->rtp)
      • ast_rtp_destroy(p->rtp);
      • p->rtp = NULL;
    • if (p->owner)
      • ast_queue_hangup(p->owner, 1);
    • transmit_response(p, "200 OK", req);
  • else if (!strcasecmp(cmd, "REGISTER"))
    • REGISTER request
    • copy_request(&p->initreq, req);
    • check_via(p, req);
    • transmit_response(p, "100 Trying", req);
    • if (register_verify(p, sin, req))
      • transmit_response(p, "401 Unauthorized", &p->initreq);
    • else
      • transmit_response(p, "200 OK", req);
  • else if (!strcasecmp(cmd, "ACK"))
    • ACK request
    • if (!p->lastinvite) sip_destroy(p);
  • else if (!strcasecmp(cmd, "SIP/2.0"))
    • Reseponse messages
    • handle_response(p, respid, e + len, req);
  • else
    • transmit_response(p, "405 Method Not Allowed", req);

ast_channel_alloc

* sip_new
  • handle_request
    • sipsock_read
  • sip_request
    • ast_channel_register(type, tdesc, capability, sip_request)
      • load_module
    • ast_request
      • dial_exec apps/app_dial.c
int load_module(void)
{
        return ast_register_application(app, dial_exec);
}

io.c

* int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)

Add a new I/O entry for this file descriptor
with the given event mask, to call callback with
data as an argument.  Returns NULL on failure.

* int ast_io_wait(struct io_context *ioc, int howlong)
  • res = poll(ioc->fds, ioc->fdcnt, howlong);
  • ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)

Sip protocol


* What are the benefits of SIP?
  • Generally it is used by two end-points to negotiate a "call."
  • By negotiate I mean
    • the medium (text, voice, other),
    • the transport (usually RTP, Real Time Protocol), and
    • the encoding (codec).
  • Once the negotiation is successful,
    • the two end-points use the selected method
    • for talking to each other - independently of SIP.
  • Once the "call" is over,
    • SIP is used to indicate a disconnection.
  • Therefore, SIP is best used as a signaling mechanism.
  • SIP and its extensions also provide related functions
    • such as instant messaging, registration, and presence.

* A sip call session between 2 phones is established as follows:
  • The calling phone sends out an invite
  • The called phone sends an information response 100 - Trying - back.
  • When the called phone starts ringing a response 180 - Ringing - is sent back
  • When the caller picks up the phone, the called phone sends a response 200 - OK
  • The calling phone responds with ACK - acknowledgement
  • Now the actual conversation is transmitted as data via RTP
  • When the person calling hangs up, a BYE request is sent to the calling phone
  • The calling phone responds with a 200 - OK.

* Call-ID
  • The Call-ID is seen as a unique identifier for a particular call.
  • It is then used by the originating client to match responses appropriately.
  • Call-ID acts as a unique identifier to group together a series of messages.
  • So what does this group means?
    • A Sip call consists of a series of requests/responses.
    • Clients (both client and server) need a mechanism to associate(group) such requests/responses.
* Components of sip
  • User Agent Client (UAC)
  • User Agent Server (UAS)
    • Proxy server
    • Redirect server
    • Registra
    • Location server
* SIP request and reply
  • start line
    • request INVITE sip:user@sipserver.com SIP/2.0
    • reply SIP/2.0 200 OK
  • message headers
    • Via
    • From
    • To
    • Call-id
    • Cseq
    • Contact
    • User-Agent
    • Content-Type
    • Content-Length
  • message body
* Request methods
  • INVITE : Indicates that the user or service is being invited to participate in a session. The body of this message would include a description of the session to which the callee is being invited.
  • ACK : Confirms that the client has received a final response to an INVITE request, and is only used with INVITE requests.
  • BYE : Is sent by a User Agent Client to indicate to the server that it wishes to terminate the call.
  • CANCEL : Is used to cancel a pending request.
  • OPTIONS : Is used to query a server about its capabilities.
  • REGISTER : Is used by a client to register an address with a SIP server.
  • INFO
* Response classes
  • provisional
  • final

Cseq

* A CSeq header field in a request contains
  • a single decimal sequence number
  • and the request method
* The CSeq header field serves
  • to identify and order transactions within a dialog,
  • to provide a means to uniquely identify transactions,
  • and to differentiate between new requests and request retransmissions.
* Two CSeq header fields are considered equal
  • if the sequence number and the request method are identical.

* Method
  • The method part of CSeq is case-sensitive and MUST match that of the request.
* Sequence number
  • is chosen by the requesting client
  • and is unique within a single value of Call-ID.
  • MUST be expressible as a 32-bit unsigned integer
  • and MUST be less than 2**31.
  • For non-REGISTER requests outside of a dialog,
    • the sequence number value is arbitrary.
  • Consecutive Requests that differ in method, headers or body, but have the same CallIdHeader
    • must contain strictly monotonically increasing
    • and contiguous sequence numbers;
    • sequence numbers do not wrap around.
  • Retransmissions of the same Request carry the same sequence number,
  • but an INVITE Request with a different message body or different headers (a "re-invitation") acquires a new, higher sequence number.
  • A server must echo the CSeqHeader from the Request in its Response.
  • If the method value is missing in the received CSeqHeader,
    • the server fills it in appropriately.
  • ACK and CANCEL Requests must contain the same CSeqHeader sequence number (but not method) as the INVITE Request they refer to,
  • while a BYE Request cancelling an invitation must have a higher sequence number.
  • An user agent server must remember the highest sequence number for any INVITE Request with the same CallIdHeader.
  • The server must respond to, and then discard, any INVITE Request with a lower sequence number.

sip address

* sip address
  • A SIP address is a way to be reachable and to reach people. You can compare it to an e-mail address. You can signup for a free account on Ekiga.net. It will give you a unique SIP address that you can give to your friends so that they can contact you. An example of SIP address is sip:dsandras@ekiga.net.
* sip
  • SIP is the Session Initiation Protocol, defined in RFC 3261. It is the most widely used standard for VoIP signalling. It was designed to be a general-purpose way to set up real-time multimedia sessions between groups of participants. SIP is used during and after the call setup.
* sip URI
  • URI Means "Uniform Resource Identifier". A SIP URI is used to identify an entity to be addressed via SIP, just like an email address identifies the email recipient. The general format of a SIP URI is user@domain:port user" identifies the particular user to address. The user can be a phone number, if this is supported by the SIP server.
Your ¡°SIP program¡± registers its online presence with a ¡°SIP Proxy¡±: ¡°Hey, i¡¯m on my home network right now, and I can be reached at this IP address¡±. When you arrive at work, your SIP program will now say ¡°Yoohoo, i¡¯ve moved, i¡¯m now here!¡±.

If someone wants to call you, they¡¯ll type your SIP address in their SIP program. The SIP provider will help this person¡¯s SIP software get in touch with your computer¡¯s SIP software, partly thanks to some STUN magic thrown in the middle. A SIP address looks exactly like an e-mail address, and, with some providers such as EarthLink, can very-well be one and the same. In my case, you can send me an e-mail at hollandct@earthlink.net or plug hollandct@earthlink.net ( or sip:hollandct@earthlink.net ) in your SIP program to call me up. If i¡¯m not online or available, you¡¯ll hear my voicemail, which will then be delivered as a .wav attachment to my e-mail address ¡¦ which Mail.app plays inline just fine!

You don¡¯t even need a ¡°SIP Provider¡± to do SIP. If you know your party¡¯s IP address or host name, if their SIP software is properly configured, you can plug their IP address into your SIP program to give them a ring.

Having a SIP address just gives you a more universal way for people to get in touch with you.



sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2008-09-03 09:40:52
Processing time 0.1182 sec