· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Asterisk Ver0-2-0/Chan Sip

AsteriskVer0-2-0/channels/chan_sip.c


* 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->iorx.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->header0;
  • 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);
}

ast_channel_register

* 0.2.0
  • static char *type = "sip";
  • static char *tdesc = "Session Initiation Protocol (SIP)";
  • static int capability = AST_FORMAT_ULAW;
  • static struct ast_channel *sip_request(char *type, int format, void *data)
  • ast_channel_register(type, tdesc, capability, sip_request)

struct chanlist {
        char type[80];
        char description[80];
        int capabilities;
        struct ast_channel * (*requester)(char *type, int format, void *data);
        struct chanlist *next;
} *backends = NULL;
* int ast_channel_register(char *type, char *description, int capabilities, struct ast_channel *(*requester)(char *type, int format, void *data))
  • chan = backends;
  • while(chan) {
    • channel type 이 이미 registered ㅤㄷㅚㅆ으면, return -1
    • 아니면 last = chan; chan = chan->next;
  • chan = malloc(sizeof(struct chanlist));
  • chan 에 type, description, capabilities, requester 을 설정
  • return 0

* struct ast_channel *ast_request(char *type, int format, void *data)
  • chanlist 에 lock 을 걸 수 없으면 return NULL
  • chan = backends;
  • while(chan) {
    • type 이 맞는 channel 을 찾는다.
    • capabilities 에 format 가 있는지 찾는다.
    • requester 이 있으면, 이를 수행시킨다. ( thread 생성)
  • }
  • type 이 맞는 channel 이 없으면 return NULL

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->iorx.callback(ioc->iorx.id, ioc->fdsx.fd, ioc->fdsx.revents, ioc->iorx.data)

sip_call


* static int sip_call(struct ast_channel *ast, char *dest, int timeout)
  • state 가 down 도 아니고 reserved 도 아니면 return -1;
  • res = 0;
  • p->outgoing = 1;
  • transmit_invite_with_sdp(p, p->username);
  • return res;

* static int transmit_invite_with_sdp(struct sip_pvt *p, char *username)
  • request messag를 만들고 send_request(p, &resp); 함.

ast_setstate


* static int sip_answer(struct ast_channel *ast)
  • if (ast->_state != AST_STATE_UP) {
    • ast_setstate(ast, AST_STATE_UP);
    • res = transmit_response_with_sdp(p, "200 OK", &p->initreq);
  • return res;
  • state 가 up 이면 아무것도 하지 않음.

* static struct ast_channel *sip_new(struct sip_pvt *i, int state)
  • ast_setstate(tmp, state);

* static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req)
  • switch(resp) {
  • case 180:
    • state 가 up 이 아니면 ast_setstate(p->owner, AST_STATE_RINGING);
  • case 200:
    • state 가 up 이 아니면 ast_setstate(p->owner, AST_STATE_UP);

ID
Password
Join
You will step on the soil of many countries.


sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2009-08-08 13:49:50
Processing time 0.0119 sec