Version 0.2.0 from FTP
authorMark Spencer <markster@digium.com>
Thu, 12 Sep 2002 17:13:17 +0000 (17:13 +0000)
committerMark Spencer <markster@digium.com>
Thu, 12 Sep 2002 17:13:17 +0000 (17:13 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@525 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c

index 1378825..b0cec81 100755 (executable)
@@ -29,6 +29,8 @@
 #include <asterisk/acl.h>
 #include <asterisk/callerid.h>
 #include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -75,7 +77,7 @@ static int restart_monitor(void);
 static int capability = AST_FORMAT_ULAW;
 
 static char ourhost[256];
-static struct in_addr ourip;
+static struct in_addr __ourip;
 static int ourport;
 
 /* Expire slowly */
@@ -99,6 +101,7 @@ struct sip_request {
 };
 
 static struct sip_pvt {
+       pthread_mutex_t lock;                           /* Channel private lock */
        char callid[80];                                        /* Global CallID */
        unsigned int cseq;                                                      /* Current seqno */
        int lastinvite;                                         /* Last Cseq of invite */
@@ -108,11 +111,19 @@ static struct sip_pvt {
        int outgoing;                                           /* Outgoing or incoming call? */
        int insecure;                                           /* Don't check source port/ip */
        int expirey;                                            /* How long we take to expire */
+       int branch;                                                     /* One random number */
+       int tag;                                                        /* Another random number */
        struct sockaddr_in sa;                          /* Our peer */
+       struct in_addr ourip;                           /* Our IP */
        struct ast_channel *owner;                      /* Who owns us */
        char exten[AST_MAX_EXTENSION];          /* Extention where to start */
        char context[AST_MAX_EXTENSION];
        char language[MAX_LANGUAGE];
+       char theirtag[256];                             /* Their tag */
+       char username[81];
+       char callerid[256];                                     /* Caller*ID */
+       char accountcode[256];                          /* Account code */
+       int amaflags;                                           /* AMA Flags */
        struct sip_request initreq;                     /* Initial request */
        struct ast_rtp *rtp;                            /* RTP Session */
        struct sip_pvt *next;
@@ -175,21 +186,9 @@ static int sipsock  = -1;
 
 static struct sockaddr_in bindaddr;
 
-#ifdef SIPDUMPER
-
-static void sip_dump_packet(char *data, int len)
-{
-       printf("SIP Packet Dump\n");
-       printf("================\n");
-       printf("Data: \n%s\n", data);
-       fflush(stdout);
-}
-
-#endif
-
 static struct ast_frame  *sip_read(struct ast_channel *ast);
-static int transmit_response(struct sip_pvt *p, char *msg);
-static int transmit_response_with_sdp(struct sip_pvt *p, char *msg);
+static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req);
+static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req);
 static int transmit_request(struct sip_pvt *p, char *msg, int inc);
 static int transmit_invite_with_sdp(struct sip_pvt *p, char *msg);
 
@@ -206,7 +205,7 @@ static int __sip_xmit(struct sip_pvt *p, char *data, int len)
 static int send_response(struct sip_pvt *p, struct sip_request *req)
 {
        int res;
-       printf("Transmitting:\n%s\n", req->data);
+       printf("Transmitting:\n%s\n to %s:%d\n", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
        res = __sip_xmit(p, req->data, req->len);
        if (res > 0)
                res = 0;
@@ -296,34 +295,16 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
 {
        int res;
        struct sip_pvt *p;
-       char *ext, *host;
-       char tmp[256];
-       char username[81];
-       strncpy(tmp, dest, sizeof(tmp) - 1);
        
        p = ast->pvt->pvt;
-       if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
+       if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
                ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
                return -1;
        }
-       host = strchr(tmp, '@');
-       if (host) {
-               *host = '\0';
-               host++;
-               ext = tmp;
-       } else {
-               host = tmp;
-               ext = NULL;
-       }
-       if (create_addr(&p->sa, &p->capability, host, username, &p->insecure)) {
-               return -1;
-       }
-       if (!ext && strlen(username))
-               ext = username;
+
        res = 0;
        p->outgoing = 1;
-       transmit_invite_with_sdp(p, username);
-       printf("Calling extension '%s' at '%s'\n", ext ? ext : "<none>", host);
+       transmit_invite_with_sdp(p, p->username);
        return res;
 }
 
@@ -333,6 +314,13 @@ static void __sip_destroy(struct sip_pvt *p)
        if (p->rtp) {
                ast_rtp_destroy(p->rtp);
        }
+       /* Unlink us from the owner if we have one */
+       if (p->owner) {
+               ast_pthread_mutex_lock(&p->owner->lock);
+               ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
+               p->owner->pvt->pvt = NULL;
+               ast_pthread_mutex_unlock(&p->owner->lock);
+       }
        cur = iflist;
        while(cur) {
                if (cur == p) {
@@ -357,29 +345,133 @@ static void sip_destroy(struct sip_pvt *p)
        ast_pthread_mutex_unlock(&iflock);
 }
 
+/* Interface lookup code courtesy Tilghman of DrunkCoder.com.  Thanks! */
+
+struct my_ifreq {
+    union
+      {
+       char ifrn_name[IFNAMSIZ];       /* Interface name, e.g. "en0".  */
+      } ifr_ifrn;
+
+    union
+      {
+       struct sockaddr_in ifru_addr;
+       char ifru_data[512];
+      } ifr_ifru;
+};
+
+struct in_addr *lookup_iface(char *iface) {
+       int mysock;
+       int res;
+       static struct  my_ifreq ifreq;
+       strncpy(ifreq.ifr_ifrn.ifrn_name,iface,sizeof(ifreq.ifr_ifrn.ifrn_name));
+
+       mysock = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
+       res = ioctl(mysock,SIOCGIFADDR,&ifreq);
+       
+       close(mysock);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
+               return &__ourip;
+       }
+       return( (struct in_addr *) &ifreq.ifr_ifru.ifru_addr.sin_addr );
+}
+
+static struct in_addr *myaddrfor(struct in_addr *them)
+{
+       FILE *PROC;
+       struct in_addr *temp = NULL;
+       unsigned int remote_ip;
+       char line[256];
+       remote_ip = them->s_addr;
+       
+       PROC = fopen("/proc/net/route","r");
+       if (!PROC) {
+               /* If /proc/net/route doesn't exist, fall back to the old method */
+               return &__ourip;
+       }
+       /* First line contains headers */
+       fgets(line,sizeof(line),PROC);
+
+       while (!feof(PROC)) {
+               char iface[8];
+               unsigned int dest, gateway, mask;
+               int i,aoffset;
+               char *fields[10];
+
+               fgets(line,sizeof(line),PROC);
+
+               aoffset = 0;
+               for (i=0;i<sizeof(line);i++) {
+                       char *boffset;
+
+                       fields[aoffset++] = line + i;
+                       boffset = strchr(line + i,'\t');
+                       if (boffset == NULL) {
+                               /* Exit loop */
+                               break;
+                       } else {
+                               *boffset = '\0';
+                               i = boffset - line;
+                       }
+               }
+
+               sscanf(fields[0],"%s",iface);
+               sscanf(fields[1],"%x",&dest);
+               sscanf(fields[2],"%x",&gateway);
+               sscanf(fields[7],"%x",&mask);
+#if 0
+               printf("Addr: %s %08x Dest: %08x Mask: %08x\n", inet_ntoa(*them), remote_ip, dest, mask);
+#endif         
+               if (((remote_ip & mask) ^ dest) == 0) {
+
+                       printf("Interface is %s\n",iface);
+                       temp = lookup_iface(iface);
+                       printf("IP Address is %s\n",inet_ntoa(*temp));
+                       break;
+               }
+       }
+       fclose(PROC);
+       if (!temp) {
+               ast_log(LOG_WARNING, "Couldn't figure out how to get to %s.  Using default\n", inet_ntoa(*them));
+               temp = &__ourip;
+       }
+       return temp;
+}
+
 
 static int sip_hangup(struct ast_channel *ast)
 {
-       struct sip_pvt *p;
+       struct sip_pvt *p = ast->pvt->pvt;
+       int needcancel = 0;
        if (option_debug)
                ast_log(LOG_DEBUG, "sip_hangup(%s)\n", ast->name);
        if (!ast->pvt->pvt) {
-               ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+               ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
                return 0;
        }
+       ast_pthread_mutex_lock(&p->lock);
+       /* Determine how to disconnect */
+       if (!p->owner || p->owner->_state != AST_STATE_UP)
+               needcancel = 1;
+       /* Disconnect */
        p = ast->pvt->pvt;
+       p->owner = NULL;
+       ast->pvt->pvt = NULL;
+
+       p->needdestroy = 1;
+       p->outgoing = 0;
+       /* Start the process if it's not already started */
        if (!p->alreadygone && strlen(p->initreq.data)) {
-               if (!p->owner || p->owner->state != AST_STATE_UP)
+               if (needcancel) {
+                       p->outgoing = 1;
                        transmit_request(p, "CANCEL", 0);
-               else
+               } else {
                        /* Send a hangup */
                        transmit_request(p, "BYE", 1);
+               }
        }
-       p->needdestroy = 1;
-       p->owner = NULL;
-       p->outgoing = 0;
-       ast->pvt->pvt = NULL;
-       printf("SIP Hangup!\n");
+       ast_pthread_mutex_unlock(&p->lock);
        return 0;
 }
 
@@ -387,38 +479,60 @@ static int sip_answer(struct ast_channel *ast)
 {
        int res = 0;
        struct sip_pvt *p = ast->pvt->pvt;
-       if (ast->state != AST_STATE_UP) {
-               ast->state = AST_STATE_UP;
+       if (ast->_state != AST_STATE_UP) {
+               ast_setstate(ast, AST_STATE_UP);
                if (option_debug)
                        ast_log(LOG_DEBUG, "sip_answer(%s)\n", ast->name);
-               res = transmit_response_with_sdp(p, "200 OK");
+               res = transmit_response_with_sdp(p, "200 OK", &p->initreq);
        }
        return res;
 }
 
 static struct ast_frame  *sip_read(struct ast_channel *ast)
 {
-       ast_log(LOG_WARNING, "I should never get called but am on %s!\n", ast->name);
-       return NULL;
+       static struct ast_frame f = { AST_FRAME_NULL, };
+       ast_log(LOG_DEBUG, "I should never get called but am on %s!\n", ast->name);
+       return &f;
 }
 
 static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 {
        struct sip_pvt *p = ast->pvt->pvt;
-       if (p->rtp) {
-               return ast_rtp_write(p->rtp, frame);
+       int res = 0;
+       if (frame->frametype != AST_FRAME_VOICE) {
+               if (frame->frametype == AST_FRAME_IMAGE)
+                       return 0;
+               else {
+                       ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
+                       return 0;
+               }
+       } else {
+               if (!(frame->subclass & ast->nativeformats)) {
+                       ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
+                               frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
+                       return -1;
+               }
        }
-       return 0;
+       if (p) {
+               ast_pthread_mutex_lock(&p->lock);
+               if (p->rtp) {
+                       res =  ast_rtp_write(p->rtp, frame);
+               }
+               ast_pthread_mutex_unlock(&p->lock);
+       }
+       return res;
 }
 
 static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
        struct sip_pvt *p = newchan->pvt->pvt;
+       ast_pthread_mutex_lock(&p->lock);
        if (p->owner != oldchan) {
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
                return -1;
        }
        p->owner = newchan;
+       ast_pthread_mutex_unlock(&p->lock);
        return 0;
 }
 
@@ -427,22 +541,22 @@ static int sip_indicate(struct ast_channel *ast, int condition)
        struct sip_pvt *p = ast->pvt->pvt;
        switch(condition) {
        case AST_CONTROL_RINGING:
-               if (ast->state == AST_STATE_RING) {
-                       transmit_response(p, "180 Ringing");
+               if (ast->_state == AST_STATE_RING) {
+                       transmit_response(p, "180 Ringing", &p->initreq);
                } else {
                        ast_log(LOG_WARNING, "XXX Need to send in-band ringtone XXX\n");
                }
                break;
        case AST_CONTROL_BUSY:
-               if (ast->state != AST_STATE_UP) {
-                       transmit_response(p, "600 Busy everywhere");
+               if (ast->_state != AST_STATE_UP) {
+                       transmit_response(p, "600 Busy everywhere", &p->initreq);
                } else {
                        ast_log(LOG_WARNING, "XXX Need to send in-band busy tone XXX\n");
                }
                break;
        case AST_CONTROL_CONGESTION:
-               if (ast->state != AST_STATE_UP) {
-                       transmit_response(p, "486 Busy here");
+               if (ast->_state != AST_STATE_UP) {
+                       transmit_response(p, "486 Busy here", &p->initreq);
                } else {
                        ast_log(LOG_WARNING, "XXX Need to send in-band congestion tone XXX\n");
                }
@@ -465,7 +579,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state)
                fmt = ast_best_codec(tmp->nativeformats);
                snprintf(tmp->name, sizeof(tmp->name), "SIP/%s:%d", inet_ntoa(i->sa.sin_addr), ntohs(i->sa.sin_port));
                tmp->type = type;
-               tmp->state = state;
+               ast_setstate(tmp, state);
                if (state == AST_STATE_RING)
                        tmp->rings = 1;
                tmp->writeformat = fmt;
@@ -535,39 +649,64 @@ static char *get_sdp(struct sip_request *req, char *name)
        return "";
 }
 
-static char *get_header(struct sip_request *req, char *name)
+static char *__get_header(struct sip_request *req, char *name, int *start)
 {
        int x;
        int len = strlen(name);
        char *r;
-       for (x=0;x<req->headers;x++) {
+       for (x=*start;x<req->headers;x++) {
                if (!strncasecmp(req->header[x], name, len) && 
                                (req->header[x][len] == ':')) {
                                        r = req->header[x] + len + 1;
                                        while(*r && (*r < 33))
                                                        r++;
+                                       *start = x+1;
                                        return r;
                }
        }
        /* Try aliases */
        for (x=0;x<sizeof(aliases) / sizeof(aliases[0]); x++) 
-               if (!strcasecmp(aliases[x].fullname, name)) 
-                       return get_header(req, aliases[x].shortname);
+               if (!strcasecmp(aliases[x].fullname, name))
+                       return __get_header(req, aliases[x].shortname, start);
 
        /* Don't return NULL, so get_header is always a valid pointer */
        return "";
 }
 
+static char *get_header(struct sip_request *req, char *name)
+{
+       int start = 0;
+       return __get_header(req, name, &start);
+}
+
 static int rtpready(struct ast_rtp *rtp, struct ast_frame *f, void *data)
 {
        /* Just deliver the audio directly */
        struct sip_pvt *p = data;
-       if (p->owner)
-               ast_queue_frame(p->owner, f, 1);
+       ast_pthread_mutex_lock(&p->lock);
+       if (p->owner) {
+               /* Generally, you lock in the order channel lock, followed by private
+                  lock.  Since here we are doing the reverse, there is the possibility
+                  of deadlock.  As a result, in the case of a deadlock, we simply fail out
+                  here. */
+               if (!pthread_mutex_trylock(&p->owner->lock)) {
+                       if (f->frametype == AST_FRAME_VOICE) {
+                               if (f->subclass != p->owner->nativeformats) {
+                                       ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
+                                       p->owner->nativeformats = f->subclass;
+                                       ast_set_read_format(p->owner, p->owner->readformat);
+                                       ast_set_write_format(p->owner, p->owner->writeformat);
+                               }
+                       }
+                       ast_queue_frame(p->owner, f, 0);
+                       pthread_mutex_unlock(&p->owner->lock);
+               }
+       }
+       ast_pthread_mutex_unlock(&p->lock);
        return 0;
 }
 
-static void build_callid(char *callid, int len)
+static void build_callid(char *callid, int len, struct in_addr ourip)
 {
        int res;
        int val;
@@ -578,32 +717,37 @@ static void build_callid(char *callid, int len)
                len -= res;
                callid += res;
        }
+       /* It's not important that we really use our right IP here... */
        snprintf(callid, len, "@%s", inet_ntoa(ourip));
 }
 
 static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin)
 {
        struct sip_pvt *p;
+
        p = malloc(sizeof(struct sip_pvt));
        if (!p)
                return NULL;
        /* Keep track of stuff */
        memset(p, 0, sizeof(struct sip_pvt));
-       if (!callid)
-               build_callid(p->callid, sizeof(p->callid));
-       else
-               strncpy(p->callid, callid, sizeof(p->callid) - 1);
-       if (sin)
-               memcpy(&p->sa, sin, sizeof(p->sa));
        p->rtp = ast_rtp_new(sched, io);
-       ast_rtp_set_data(p->rtp, p);
-       ast_rtp_set_callback(p->rtp, rtpready);
        if (!p->rtp) {
                ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno));
                free(p);
                return NULL;
        }
-       
+       ast_pthread_mutex_init(&p->lock);
+       ast_rtp_set_data(p->rtp, p);
+       ast_rtp_set_callback(p->rtp, rtpready);
+       if (sin) {
+               memcpy(&p->sa, sin, sizeof(p->sa));
+               memcpy(&p->ourip, myaddrfor(&p->sa.sin_addr), sizeof(p->ourip));
+       } else
+               memcpy(&p->ourip, &__ourip, sizeof(p->ourip));
+       if (!callid)
+               build_callid(p->callid, sizeof(p->callid), p->ourip);
+       else
+               strncpy(p->callid, callid, sizeof(p->callid) - 1);
        /* Add to list */
        ast_pthread_mutex_lock(&iflock);
        p->next = iflist;
@@ -847,6 +991,27 @@ static int copy_header(struct sip_request *req, struct sip_request *orig, char *
        return -1;
 }
 
+static int copy_all_header(struct sip_request *req, struct sip_request *orig, char *field)
+{
+       char *tmp;
+       int start = 0;
+       int copied = 0;
+       for (;;) {
+               tmp = __get_header(orig, field, &start);
+               if (strlen(tmp)) {
+                       /* Add what we're responding to */
+                       add_header(req, field, tmp);
+                       copied++;
+               } else
+                       break;
+       }
+       if (!copied) {
+               ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
+               return -1;
+       }
+       return 0;
+}
+
 static int init_resp(struct sip_request *req, char *resp, struct sip_request *orig)
 {
        /* Initialize a response */
@@ -879,14 +1044,26 @@ static int init_req(struct sip_request *req, char *resp, char *recip)
        return 0;
 }
 
-static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg)
+static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, struct sip_request *req)
 {
-       struct sip_request *req = &p->initreq;
+       char newto[256], *ot;
        memset(resp, 0, sizeof(*resp));
        init_resp(resp, msg, req);
-       copy_header(resp, req, "Via");
+       copy_all_header(resp, req, "Via");
        copy_header(resp, req, "From");
-       copy_header(resp, req, "To");
+       ot = get_header(req, "To");
+       if (!strstr(ot, "tag=")) {
+               /* Add the proper tag if we don't have it already.  If they have specified
+                  their tag, use it.  Otherwise, use our own tag */
+               if (strlen(p->theirtag))
+                       snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
+               else if (p->tag)
+                       snprintf(newto, sizeof(newto), "%s;tag=%08x", ot, p->tag);
+               else
+                       strncpy(newto, ot, sizeof(newto) - 1);
+               ot = newto;
+       }
+       add_header(resp, "To", ot);
        copy_header(resp, req, "Call-ID");
        copy_header(resp, req, "CSeq");
        add_header(resp, "User-Agent", "Asterisk PBX");
@@ -896,15 +1073,19 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg)
                char tmp[80];
                char contact2[256], *c, contact[256];
                snprintf(tmp, sizeof(tmp), "%d", p->expirey);
-#if 1
-               /* XXX This isn't exactly right and it's implemented
-                      very stupidly *sigh* XXX */
                strncpy(contact2, get_header(req, "Contact"), sizeof(contact2)-1);
                c = ditch_braces(contact2);
                snprintf(contact, sizeof(contact), "<%s>", c);
-#endif
                add_header(resp, "Expires", tmp);
                add_header(resp, "Contact", contact);
+       } else {
+               char contact2[256], *c, contact[256];
+               /* XXX This isn't exactly right and it's implemented
+                      very stupidly *sigh* XXX */
+               strncpy(contact2, get_header(req, "To"), sizeof(contact2)-1);
+               c = ditch_braces(contact2);
+               snprintf(contact, sizeof(contact), "<%s>", c);
+               add_header(resp, "Contact", contact);
        }
        return 0;
 }
@@ -914,6 +1095,7 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int in
        struct sip_request *orig = &p->initreq;
        char stripped[80];
        char tmp[80];
+       char newto[256];
        char *c, *n;
        char *ot, *of;
 
@@ -939,12 +1121,22 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int in
        init_req(req, msg, c);
 
        snprintf(tmp, sizeof(tmp), "%d %s", p->cseq, msg);
-       add_header(req, "CSeq", tmp);
 
-       copy_header(req, orig, "Via");
+       copy_all_header(req, orig, "Via");
 
        ot = get_header(orig, "To");
        of = get_header(orig, "From");
+
+       if (!strstr(ot, "tag=")) {
+               /* Add the proper tag if we don't have it already.  If they have specified
+                  their tag, use it.  Otherwise, use our own tag */
+               if (strlen(p->theirtag))
+                       snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
+               else
+                       snprintf(newto, sizeof(newto), "%s;tag=%08x", ot, p->tag);
+               ot = newto;
+       }
+
        if (p->outgoing) {
                add_header(req, "From", of);
                add_header(req, "To", ot);
@@ -954,14 +1146,16 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int in
        }
 
        copy_header(req, orig, "Call-ID");
+       add_header(req, "CSeq", tmp);
+
        add_header(req, "User-Agent", "Asterisk PBX");
        return 0;
 }
 
-static int transmit_response(struct sip_pvt *p, char *msg)
+static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req)
 {
        struct sip_request resp;
-       respprep(&resp, p, msg);
+       respprep(&resp, p, msg, req);
        add_header(&resp, "Content-Length", "0");
        add_blank_header(&resp);
        return send_response(p, &resp);
@@ -979,38 +1173,45 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p)
        char c[256];
        char t[256];
        char m[256];
+       char a[1024] = "";
        int x;
        /* XXX We break with the "recommendation" and send our IP, in order that our
               peer doesn't have to gethostbyname() us XXX */
        len = 0;
        ast_rtp_get_us(p->rtp, &sin);
-       printf("We're at %s port %d\n", inet_ntoa(ourip), ntohs(sin.sin_port)); 
+       printf("We're at %s port %d\n", inet_ntoa(p->ourip), ntohs(sin.sin_port));      
        snprintf(v, sizeof(v), "v=0\r\n");
-       snprintf(s, sizeof(s), "s=Asterisk Call from %s\r\n", ourhost);
-       snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), inet_ntoa(ourip));
-       snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", inet_ntoa(ourip));
+       snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), inet_ntoa(p->ourip));
+       snprintf(s, sizeof(s), "s=session\r\n");
+       snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", inet_ntoa(p->ourip));
        snprintf(t, sizeof(t), "t=0 0\r\n");
-       snprintf(m, sizeof(m), "m=audio %d RTP/AVP 101", ntohs(sin.sin_port));
+       snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(sin.sin_port));
        for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
                if (p->capability & x) {
                        printf("Answering with capability %d\n", x);
                        if ((codec = ast2rtp(x)) > -1) {
                                snprintf(costr, sizeof(costr), " %d", codec);
                                strcat(m, costr);
+                               snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast2rtpn(x));
+                               strcat(a, costr);
                        }
                }
        }
-       strcat(m, "\r\n");
-       len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m);
+       strcat(m, " 101\r\n");
+       strcat(a, "a=rtpmap:101 telephone-event/8000\r\n");
+       /* Indicate we support DTMF only...  Not sure about 16, but MSN supports it so dang it, we will too... */
+       strcat(a, "a=fmtp:101 0-16\r\n");
+       len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a);
        snprintf(costr, sizeof(costr), "%d", len);
        add_header(resp, "Content-Type", "application/sdp");
        add_header(resp, "Content-Length", costr);
        add_line(resp, v);
-       add_line(resp, s);
        add_line(resp, o);
+       add_line(resp, s);
        add_line(resp, c);
        add_line(resp, t);
        add_line(resp, m);
+       add_line(resp, a);
        return 0;
 }
 
@@ -1028,10 +1229,10 @@ static void copy_request(struct sip_request *dst,struct sip_request *src)
                dst->line[x] += offset;
 }
 
-static int transmit_response_with_sdp(struct sip_pvt *p, char *msg)
+static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req)
 {
        struct sip_request resp;
-       respprep(&resp, p, msg);
+       respprep(&resp, p, msg, req);
        add_sdp(&resp, p);
        return send_response(p, &resp);
 }
@@ -1053,22 +1254,33 @@ static int transmit_invite_with_sdp(struct sip_pvt *p, char *username)
        }
        if (!n)
                n = "";
-       snprintf(from, sizeof(from), "\"%s\" <sip:sip@%s>;tag=%08x", n, inet_ntoa(ourip), rand());
-       if (strlen(username))
-               snprintf(to, sizeof(to), " sip:%s@%s",username, inet_ntoa(p->sa.sin_addr));
-       else
-               snprintf(to, sizeof(to), " sip:%s", inet_ntoa(p->sa.sin_addr));
-       snprintf(via, sizeof(via), "SIP/2.0/UDP %s:%d", inet_ntoa(ourip), ourport);
-       memset(&req, 0, sizeof(req));
+       p->branch = rand();     
+       p->tag = rand();
+       snprintf(from, sizeof(from), "\"%s\" <sip:sip@%s>;tag=%08x", n, inet_ntoa(p->ourip), p->tag);
+       if (strlen(username)) {
+               if (ntohs(p->sa.sin_port) != DEFAULT_SIP_PORT) {
+                       snprintf(to, sizeof(to), "<sip:%s@%s:%d>",username, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
+               } else {
+                       snprintf(to, sizeof(to), "<sip:%s@%s>",username, inet_ntoa(p->sa.sin_addr));
+               }
+       } else if (ntohs(p->sa.sin_port) != DEFAULT_SIP_PORT) {
+               snprintf(to, sizeof(to), "<sip:%s:%d>", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
+       } else {
+               snprintf(to, sizeof(to), "<sip:%s>", inet_ntoa(p->sa.sin_addr));
+       }
+       snprintf(via, sizeof(via), "SIP/2.0/UDP %s:%d;branch=%08x", inet_ntoa(p->ourip), ourport, p->branch);
 
+       memset(&req, 0, sizeof(req));
        init_req(&req, "INVITE", to);
+       /* Start with 101 instead of 1 */
+       p->cseq = 100;
        snprintf(tmp, sizeof(tmp), "%d %s", ++p->cseq, "INVITE");
-       add_header(&req, "CSeq", tmp);
 
-       add_header(&req, "To", to);
+       add_header(&req, "Via", via);
        add_header(&req, "From", from);
+       add_header(&req, "To", to);
        add_header(&req, "Call-ID", p->callid);
-       add_header(&req, "Via", via);
+       add_header(&req, "CSeq", tmp);
        add_header(&req, "User-Agent", "Asterisk PBX");
        add_sdp(&req, p);
        /* Use this as the basis */
@@ -1223,6 +1435,11 @@ static int get_destination(struct sip_pvt *p)
                strncpy(p->exten, c, sizeof(p->exten));
                return 0;
        }
+
+       if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
+               return 1;
+       }
+       
        return -1;
 }
 
@@ -1231,11 +1448,17 @@ static int check_via(struct sip_pvt *p, struct sip_request *req)
        char via[256];
        char *c, *pt;
        struct hostent *hp;
+
        strncpy(via, get_header(req, "Via"), sizeof(via));
+       c = strchr(via, ';');
+       if (c) 
+               *c = '\0';
        c = strchr(via, ' ');
        if (c) {
                *c = '\0';
                c++;
+               while(*c && (*c < 33))
+                       c++;
                if (strcmp(via, "SIP/2.0/UDP")) {
                        ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
                        return -1;
@@ -1259,34 +1482,81 @@ static int check_via(struct sip_pvt *p, struct sip_request *req)
        return 0;
 }
 
+static int check_user(struct sip_pvt *p, struct sip_request *req)
+{
+       struct sip_user *user;
+       char *of, from[256], *c;
+       of = get_header(req, "From");
+       strncpy(from, of, sizeof(from) - 1);
+       of = ditch_braces(from);
+       if (strncmp(of, "sip:", 4))
+               return 0;
+       else
+               of += 4;
+       strncpy(p->callerid, of, sizeof(p->callerid) - 1);
+       /* Get just the username part */
+       if ((c = strchr(of, '@')))
+               *c = '\0';
+       if ((c = strchr(of, ':')))
+               *c = '\0';
+       if (!strlen(of))
+                       return 0;
+       printf("From: %s\n", of);
+       ast_pthread_mutex_lock(&userl.lock);
+       user = userl.users;
+       while(user) {
+               if (!strcasecmp(user->name, of)) {
+                       strncpy(p->context, user->context, sizeof(p->context) - 1);
+                       if (strlen(user->callerid) && strlen(p->callerid)) 
+                               strncpy(p->callerid, user->callerid, sizeof(p->callerid) - 1);
+                       strncpy(p->accountcode, user->accountcode, sizeof(p->accountcode)  -1);
+                       p->amaflags = user->amaflags;
+                       printf("Context is %s\n", p->context);
+               }
+               user = user->next;
+       }
+       ast_pthread_mutex_unlock(&userl.lock);
+       return 0;
+}
+
 static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req)
 {
-       struct ast_frame f;
-       memset(&f, 0, sizeof(f));
+       char *to;
+       struct ast_rtp *rtp;
+       ast_pthread_mutex_lock(&p->lock);
        if (p->outgoing) {
+               /* Get their tag if we haven't already */
+               if (!strlen(p->theirtag)) {
+                       to = get_header(req, "To");
+                       to = strstr(to, "tag=");
+                       if (to) {
+                               to += 4;
+                               strncpy(p->theirtag, to, sizeof(p->theirtag));
+                               to = strchr(p->theirtag, ';');
+                               if (to)
+                                       *to = '\0';
+                       }
+               }
+               
                switch(resp) {
                case 100:
                        /* Not important */
                        break;
                case 180:
                        if (p->owner) {
-                               f.frametype = AST_FRAME_CONTROL;
-                               f.subclass = AST_CONTROL_RINGING;
-                               ast_queue_frame(p->owner, &f, 1);
-                               if (p->owner->state != AST_STATE_UP)
-                                       p->owner->state = AST_STATE_RINGING;
+                               ast_queue_control(p->owner, AST_CONTROL_RINGING, 1);
+                               if (p->owner->_state != AST_STATE_UP)
+                                       ast_setstate(p->owner, AST_STATE_RINGING);
                        }
                        break;
                case 200:
                        process_sdp(p, req);
                        if (p->owner) {
-                               if (p->owner->state != AST_STATE_UP) {
-                                       f.frametype = AST_FRAME_CONTROL;
-                                       f.subclass = AST_CONTROL_ANSWER;
-                                       p->owner->state = AST_STATE_UP;
-                                       ast_queue_frame(p->owner, &f, 1);
-                                       transmit_request(p, "ACK", 0);
+                               if (p->owner->_state != AST_STATE_UP) {
+                                       ast_setstate(p->owner, AST_STATE_UP);
+                                       ast_queue_control(p->owner, AST_CONTROL_ANSWER, 1);
                                }
+                               transmit_request(p, "ACK", 0);
                        }
                        break;
                default:
@@ -1295,17 +1565,23 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
                                        ast_verbose(VERBOSE_PREFIX_3 "Got SIP response %d \"%s\" back from %s\n", resp, rest, inet_ntoa(p->sa.sin_addr));
                                p->alreadygone = 1;
                                if (p->rtp) {
-                                       /* Immediately stop RTP */
-                                       ast_rtp_destroy(p->rtp);
+                                       rtp = p->rtp;
                                        p->rtp = NULL;
+                                       /* Immediately stop RTP */
+                                       ast_rtp_destroy(rtp);
                                }
                                /* Send hangup */       
                                if (p->owner)
                                        ast_queue_hangup(p->owner, 1);
+                               transmit_request(p, "ACK", 0);
+                               sip_destroy(p);
+                               p = NULL;
                        } else
                                ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : inet_ntoa(p->sa.sin_addr));
                }
        }
+       if (p)
+               ast_pthread_mutex_unlock(&p->lock);
 }
 
 static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin)
@@ -1319,6 +1595,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
        int len;
        int ignore=0;
        int respid;
+       int res;
        /* Clear out potential response */
        memset(&resp, 0, sizeof(resp));
        /* Get Method and Cseq */
@@ -1332,7 +1609,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                return -1;
        }
        if (p->cseq && (p->cseq < seqno)) {
-               ast_log(LOG_DEBUG, "Ignoring out of order packet %d\n", seqno);
+               ast_log(LOG_DEBUG, "Ignoring out of order packet %d (expecting %d)\n", seqno, p->cseq);
                return -1;
        } else if (p->cseq && (p->cseq != seqno)) {
                /* ignore means "don't do anything with it" but still have to 
@@ -1364,6 +1641,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                        /* Use this as the basis */
                        printf("Using latest request as basis request\n");
                        copy_request(&p->initreq, req);
+                       check_user(p, req);
                        check_via(p, req);
                        if (process_sdp(p, req))
                                return -1;
@@ -1373,8 +1651,11 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                        /* Initialize the context if it hasn't been already */
                        if (!strlen(p->context))
                                strncpy(p->context, context, sizeof(p->context));
-                       if (get_destination(p)) {
-                               transmit_response(p, "404 Not Found");
+                       if ((res = get_destination(p))) {
+                               if (res < 0)
+                                       transmit_response(p, "404 Not Found", req);
+                               else
+                                       transmit_response(p, "484 Address Incomplete", req);
                                sip_destroy(p);
                                p = NULL;
                                c = NULL;
@@ -1382,6 +1663,8 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                                /* If no extension was specified, use the s one */
                                if (!strlen(p->exten))
                                        strncpy(p->exten, "s", sizeof(p->exten));
+                               /* Initialize tag */    
+                               p->tag = rand();
                                /* First invitation */
                                c = sip_new(p, AST_STATE_RING);
                        }
@@ -1391,24 +1674,24 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                if (!ignore && p)
                        p->lastinvite = seqno;
                if (c) {
-                       switch(c->state) {
+                       switch(c->_state) {
                        case AST_STATE_RING:
-                               transmit_response(p, "100 Trying");
+                               transmit_response(p, "100 Trying", req);
                                break;
                        case AST_STATE_RINGING:
-                               transmit_response(p, "180 Ringing");
+                               transmit_response(p, "180 Ringing", req);
                                break;
                        case AST_STATE_UP:
-                               transmit_response_with_sdp(p, "200 OK");
+                               transmit_response_with_sdp(p, "200 OK", req);
                                break;
                        default:
-                               ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->state);
-                               transmit_response(p, "100 Trying");
+                               ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state);
+                               transmit_response(p, "100 Trying", req);
                        }
                } else {
                        if (p) {
                                ast_log(LOG_NOTICE, "Unable to create/find channel\n");
-                               transmit_response(p, "503 Unavailable");
+                               transmit_response(p, "503 Unavailable", req);
                                sip_destroy(p);
                        }
                }
@@ -1423,18 +1706,18 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                }
                if (p->owner)
                        ast_queue_hangup(p->owner, 1);
-               transmit_response(p, "200 OK");
+               transmit_response(p, "200 OK", req);
        } else if (!strcasecmp(cmd, "REGISTER")) {
                /* Use this as the basis */
                printf("Using latest request as basis request\n");
                copy_request(&p->initreq, req);
                check_via(p, req);
-               transmit_response(p, "100 Trying");
+               transmit_response(p, "100 Trying", req);
                if (register_verify(p, sin, req)) {
                        ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s'\n", get_header(req, "To"), inet_ntoa(sin->sin_addr));
-                       transmit_response(p, "401 Unauthorized");
+                       transmit_response(p, "401 Unauthorized", &p->initreq);
                } else {
-                       transmit_response(p, "200 OK");
+                       transmit_response(p, "200 OK", req);
                }
                sip_destroy(p);
        } else if (!strcasecmp(cmd, "ACK")) {
@@ -1451,6 +1734,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
                        handle_response(p, respid, e + len, req);
                }
        } else {
+               transmit_response(p, "405 Method Not Allowed", req);
                ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", 
                        cmd, inet_ntoa(p->sa.sin_addr));
        }
@@ -1465,6 +1749,7 @@ static int sipsock_read(int *id, int fd, short events, void *ignore)
        int res;
        int len;
        len = sizeof(sin);
+       memset(&req, 0, sizeof(req));
        res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
        if (res < 0) {
                if (errno != ECONNREFUSED)
@@ -1586,10 +1871,11 @@ static struct ast_channel *sip_request(char *type, int format, void *data)
 {
        int oldformat;
        struct sip_pvt *p;
-       struct ast_channel *tmp = NULL;
-       /* We can only support G.723.1 formatted frames, but we should never
-          be asked to support anything else anyway, since we've published
-          our capabilities when we registered. */
+       struct ast_channel *tmpc = NULL;
+       char *ext, *host;
+       char tmp[256];
+       char *dest = data;
+
        oldformat = format;
        format &= capability;
        if (!format) {
@@ -1601,9 +1887,32 @@ static struct ast_channel *sip_request(char *type, int format, void *data)
                ast_log(LOG_WARNING, "Unable to build sip pvt data for '%s'\n", (char *)data);
                return NULL;
        }
-       tmp = sip_new(p, AST_STATE_DOWN);
+
+       strncpy(tmp, dest, sizeof(tmp) - 1);
+       host = strchr(tmp, '@');
+       if (host) {
+               *host = '\0';
+               host++;
+               ext = tmp;
+       } else {
+               host = tmp;
+               ext = NULL;
+       }
+       if (create_addr(&p->sa, &p->capability, host, p->username, &p->insecure)) {
+               sip_destroy(p);
+               return NULL;
+       }
+       /* Recalculate our side, and recalculate Call ID */
+       memcpy(&p->ourip, myaddrfor(&p->sa.sin_addr), sizeof(p->ourip));
+       build_callid(p->callid, sizeof(p->callid), p->ourip);
+       if (ext)
+               strncpy(p->username, ext, sizeof(p->username) - 1);
+       printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
+       tmpc = sip_new(p, AST_STATE_DOWN);
+       if (!tmpc)
+               sip_destroy(p);
        restart_monitor();
-       return tmp;
+       return tmpc;
 }
 
 static struct sip_user *build_user(char *name, struct ast_variable *v)
@@ -1777,12 +2086,6 @@ int load_module()
                ast_log(LOG_WARNING, "Unable to get hostname, SIP disabled\n");
                return 0;
        }
-       hp = gethostbyname(ourhost);
-       if (!hp) {
-               ast_log(LOG_WARNING, "Unable to get our IP address, SIP disabled\n");
-               return 0;
-       }
-       memcpy(&ourip, hp->h_addr, sizeof(ourip));
        cfg = ast_load(config);
 
        /* We *must* have a config file otherwise stop immediately */
@@ -1845,6 +2148,16 @@ int load_module()
                cat = ast_category_browse(cfg, cat);
        }
        
+       if (ntohl(bindaddr.sin_addr.s_addr)) {
+               memcpy(&__ourip, &bindaddr, sizeof(__ourip));
+       } else {
+               hp = gethostbyname(ourhost);
+               if (!hp) {
+                       ast_log(LOG_WARNING, "Unable to get our IP address, SIP disabled\n");
+                       return 0;
+               }
+               memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
+       }
        if (!ntohs(bindaddr.sin_port))
                bindaddr.sin_port = ntohs(DEFAULT_SIP_PORT);
        bindaddr.sin_family = AF_INET;
@@ -1889,7 +2202,7 @@ int unload_module()
                p = iflist;
                while(p) {
                        if (p->owner)
-                               ast_softhangup(p->owner);
+                               ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
                        p = p->next;
                }
                iflist = NULL;