MGCP updates to try to improve CID delivery
[asterisk/asterisk.git] / channels / chan_mgcp.c
index 79e9620..8f4a761 100755 (executable)
@@ -39,6 +39,8 @@
 #include <netdb.h>
 #include <arpa/inet.h>
 #include <sys/signal.h>
+#include <asterisk/dsp.h>
+#include <ctype.h>
 
 #define MGCPDUMPER
 #define DEFAULT_EXPIREY 120
@@ -70,11 +72,14 @@ static int restart_monitor(void);
 
 /* Just about everybody seems to support ulaw, so make it a nice default */
 static int capability = AST_FORMAT_ULAW;
+static int nonCodecCapability = AST_RTP_DTMF;
 
 static char ourhost[256];
 static struct in_addr __ourip;
 static int ourport;
 
+static int mgcpdebug = 0;
+
 static struct sched_context *sched;
 static struct io_context *io;
 /* The private structures of the  mgcp channels are linked for
@@ -104,6 +109,14 @@ static struct mgcp_pkt {
        struct mgcp_pkt *next;
 } *packets = NULL;     
 
+/* MGCP message for queuing up */
+struct mgcp_message {
+       unsigned int seqno;
+       int len;
+       struct mgcp_message *next;
+       unsigned char buf[0];
+};
+
 #define TYPE_TRUNK             1
 #define TYPE_LINE              2
 
@@ -120,17 +133,24 @@ struct mgcp_endpoint {
        char cxident[80];
        char callid[80];
        int hascallerid;
+       int dtmfinband;
        int amaflags;
        int type;
        int group;
        int iseq;
+       int nat;
        int lastout;
        int alreadygone;
        int needdestroy;
        int capability;
+       int nonCodecCapability;
        int outgoing;
+       struct ast_dsp *vad;
        struct ast_channel *owner;
        struct ast_rtp *rtp;
+       struct sockaddr_in tmpdest;
+       struct mgcp_message *msgs;                      /* Message queue */
+       int messagepending;
        struct mgcp_endpoint *next;
        struct mgcp_gateway *parent;
 };
@@ -139,7 +159,10 @@ struct mgcp_gateway {
        /* A gateway containing one or more endpoints */
        char name[80];
        struct sockaddr_in addr;
+       struct sockaddr_in defaddr;
        struct in_addr ourip;
+       int dynamic;
+       int expire;             /* XXX Should we ever expire dynamic registrations? XXX */
        struct mgcp_endpoint *endpoints;
        struct ast_ha *ha;
        struct mgcp_gateway *next;
@@ -160,7 +183,10 @@ static int transmit_notify_request_with_callerid(struct mgcp_endpoint *p, char *
 static int __mgcp_xmit(struct mgcp_endpoint *p, char *data, int len)
 {
        int res;
-    res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->addr, sizeof(struct sockaddr_in));
+       if (p->parent->addr.sin_addr.s_addr)
+           res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->addr, sizeof(struct sockaddr_in));
+       else 
+           res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->defaddr, sizeof(struct sockaddr_in));
        if (res != len) {
                ast_log(LOG_WARNING, "mgcp_xmit returned %d: %s\n", res, strerror(errno));
        }
@@ -170,18 +196,60 @@ static int __mgcp_xmit(struct mgcp_endpoint *p, char *data, int len)
 static int send_response(struct mgcp_endpoint *p, struct mgcp_request *req)
 {
        int res;
-       printf("Transmitting:\n%s\n to %s:%d\n", req->data, inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+       if (mgcpdebug)
+               ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
        res = __mgcp_xmit(p, req->data, req->len);
        if (res > 0)
                res = 0;
        return res;
 }
 
-static int send_request(struct mgcp_endpoint *p, struct mgcp_request *req)
+static void dump_queue(struct mgcp_endpoint *p)
+{
+       struct mgcp_message *cur;
+       while(p->msgs) {
+               cur = p->msgs;
+               p->msgs = p->msgs->next;
+               free(cur);
+       }
+       p->messagepending = 0;
+       p->msgs = NULL;
+}
+
+static int mgcp_postrequest(struct mgcp_endpoint *p, unsigned char *data, int len, unsigned int seqno)
+{
+       struct mgcp_message *msg = malloc(sizeof(struct mgcp_message) + len);
+       struct mgcp_message *cur;
+       if (!msg)
+               return -1;
+       msg->seqno = seqno;
+       msg->next = NULL;
+       msg ->len = len;
+       memcpy(msg->buf, data, msg->len);
+       cur = p->msgs;
+       if (cur) {
+               while(cur->next)
+                       cur = cur->next;
+               cur->next = msg;
+       } else
+               p->msgs = msg;
+       if (!p->messagepending) {
+               p->messagepending = 1;
+               p->lastout = seqno;
+               __mgcp_xmit(p, msg->buf, msg->len);
+               /* XXX Should schedule retransmission XXX */
+       } else
+               ast_log(LOG_DEBUG, "Deferring transmission of transaction %d\n", seqno);
+       return 0;
+}
+
+static int send_request(struct mgcp_endpoint *p, struct mgcp_request *req, unsigned int seqno)
 {
        int res;
-       printf("XXX Need to handle Retransmitting XXX:\n%s to %s:%d\n", req->data, inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
-       res = __mgcp_xmit(p, req->data, req->len);
+       if (mgcpdebug)
+               ast_verbose("Posting Request:\n%s to %s:%d\n", req->data, inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+               
+       res = mgcp_postrequest(p, req->data, req->len, seqno);
        return res;
 }
 
@@ -199,7 +267,7 @@ static int mgcp_call(struct ast_channel *ast, char *dest, int timeout)
        res = 0;
        p->outgoing = 1;
        if (p->type == TYPE_LINE) {
-               transmit_notify_request_with_callerid(p, "rg", 0, ast->callerid);
+               transmit_notify_request_with_callerid(p, "L/rg", 0, ast->callerid);
                ast_setstate(ast, AST_STATE_RINGING);
                ast_queue_control(ast, AST_CONTROL_RINGING, 0);
        } else {
@@ -289,9 +357,11 @@ static struct in_addr *myaddrfor(struct in_addr *them)
 #endif         
                if (((remote_ip & mask) ^ dest) == 0) {
 
-                       printf("Interface is %s\n",iface);
+                       if (mgcpdebug)
+                                       ast_verbose("Interface is %s\n",iface);
                        temp = lookup_iface(iface);
-                       printf("IP Address is %s\n",inet_ntoa(*temp));
+                       if (mgcpdebug)
+                               ast_verbose("IP Address is %s\n",inet_ntoa(*temp));
                        break;
                }
        }
@@ -313,10 +383,14 @@ static int mgcp_hangup(struct ast_channel *ast)
                ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
                return 0;
        }
+       if ((p->dtmfinband) && (p->vad != NULL)){
+           ast_dsp_free(p->vad);
+       }
        ast_pthread_mutex_lock(&p->lock);
        p->owner = NULL;
        if (strlen(p->cxident))
                transmit_connection_del(p);
+       strcpy(p->cxident, "");
        if (!p->alreadygone && (!p->outgoing || (ast->_state == AST_STATE_UP)))
                transmit_notify_request(p, "ro", 1);
        else
@@ -324,8 +398,9 @@ static int mgcp_hangup(struct ast_channel *ast)
        ast->pvt->pvt = NULL;
        p->alreadygone = 0;
        p->outgoing = 0;
-       strcpy(p->cxident, "");
        strcpy(p->callid, "");
+       /* Reset temporary destination */
+       memset(&p->tmpdest, 0, sizeof(p->tmpdest));
        if (p->rtp) {
                ast_rtp_destroy(p->rtp);
                p->rtp = NULL;
@@ -345,7 +420,7 @@ static int mgcp_show_endpoints(int fd, int argc, char *argv[])
        g = gateways;
        while(g) {
                e = g->endpoints;
-               ast_cli(fd, "Gateway '%s' at %s\n", g->name, inet_ntoa(g->addr.sin_addr));
+               ast_cli(fd, "Gateway '%s' at %s (%s)\n", g->name, g->addr.sin_addr.s_addr ? inet_ntoa(g->addr.sin_addr) : inet_ntoa(g->defaddr.sin_addr), g->dynamic ? "Dynamic" : "Static");
                while(e) {
                        ast_cli(fd, "   -- '%s@%s in '%s' is %s\n", e->name, g->name, e->context, e->owner ? "active" : "idle");
                        hasendpoints = 1;
@@ -380,11 +455,34 @@ static int mgcp_answer(struct ast_channel *ast)
        return res;
 }
 
+static struct ast_frame *mgcp_rtp_read(struct mgcp_endpoint *p)
+{
+       /* Retrieve audio/etc from channel.  Assumes p->lock is already held. */
+       struct ast_frame *f;
+       f = ast_rtp_read(p->rtp);
+       if (p->owner) {
+               /* We already hold the channel 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);
+                       }
+               }
+       }
+       return f;
+}
+
+
 static struct ast_frame  *mgcp_read(struct ast_channel *ast)
 {
-       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;
+       struct ast_frame *fr;
+       struct mgcp_endpoint *p = ast->pvt->pvt;
+       ast_pthread_mutex_lock(&p->lock);
+       fr = mgcp_rtp_read(p);
+       ast_pthread_mutex_unlock(&p->lock);
+       return fr;
 }
 
 static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame)
@@ -473,7 +571,15 @@ static struct ast_channel *mgcp_new(struct mgcp_endpoint *i, int state)
                        tmp->nativeformats = capability;
                fmt = ast_best_codec(tmp->nativeformats);
                snprintf(tmp->name, sizeof(tmp->name), "MGCP/%s@%s", i->name, i->parent->name);
+               if (i->rtp)
+                       tmp->fds[0] = ast_rtp_fd(i->rtp);
                tmp->type = type;
+               if (i->dtmfinband) {
+                   i->vad = ast_dsp_new();
+                   ast_dsp_set_features(i->vad,DSP_FEATURE_DTMF_DETECT);
+               } else {
+                   i->vad = NULL;
+               }
                ast_setstate(tmp, state);
                if (state == AST_STATE_RING)
                        tmp->rings = 1;
@@ -490,6 +596,7 @@ static struct ast_channel *mgcp_new(struct mgcp_endpoint *i, int state)
                tmp->pvt->indicate = mgcp_indicate;
                tmp->pvt->fixup = mgcp_fixup;
                tmp->pvt->send_digit = mgcp_senddigit;
+               tmp->pvt->bridge = ast_rtp_bridge;
                if (strlen(i->language))
                        strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
                i->owner = tmp;
@@ -514,21 +621,41 @@ static struct ast_channel *mgcp_new(struct mgcp_endpoint *i, int state)
        return tmp;
 }
 
-static char *get_sdp(struct mgcp_request *req, char *name)
-{
-       int x;
-       int len = strlen(name);
-       char *r;
-       for (x=0;x<req->lines;x++) {
-               if (!strncasecmp(req->line[x], name, len) && 
-                               (req->line[x][len] == '=')) {
-                                       r = req->line[x] + len + 1;
-                                       while(*r && (*r < 33))
-                                                       r++;
-                                       return r;
-               }
-       }
-       return "";
+static char* get_sdp_by_line(char* line, char *name, int nameLen) {
+  if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') {
+    char* r = line + nameLen + 1;
+    while (*r && (*r < 33)) ++r;
+    return r;
+  }
+
+  return "";
+}
+
+static char *get_sdp(struct mgcp_request *req, char *name) {
+  int x;
+  int len = strlen(name);
+  char *r;
+
+  for (x=0; x<req->lines; x++) {
+    r = get_sdp_by_line(req->line[x], name, len);
+    if (r[0] != '\0') return r;
+  }
+  return "";
+}
+
+static void sdpLineNum_iterator_init(int* iterator) {
+  *iterator = 0;
+}
+
+static char* get_sdp_iterate(int* iterator,
+                            struct mgcp_request *req, char *name) {
+  int len = strlen(name);
+  char *r;
+  while (*iterator < req->lines) {
+    r = get_sdp_by_line(req->line[(*iterator)++], name, len);
+    if (r[0] != '\0') return r;
+  }
+  return "";
 }
 
 static char *__get_header(struct mgcp_request *req, char *name, int *start)
@@ -556,6 +683,7 @@ static char *get_header(struct mgcp_request *req, char *name)
        return __get_header(req, name, &start);
 }
 
+#if 0
 static int rtpready(struct ast_rtp *rtp, struct ast_frame *f, void *data)
 {
        /* Just deliver the audio directly */
@@ -574,6 +702,9 @@ static int rtpready(struct ast_rtp *rtp, struct ast_frame *f, void *data)
                                        ast_set_read_format(p->owner, p->owner->readformat);
                                        ast_set_write_format(p->owner, p->owner->writeformat);
                                }
+                               if (p->dtmfinband) {
+                                   f = ast_dsp_process(p->owner,p->vad,f,0);
+                               }
                        }
                        ast_queue_frame(p->owner, f, 0);
                        pthread_mutex_unlock(&p->owner->lock);
@@ -582,6 +713,7 @@ static int rtpready(struct ast_rtp *rtp, struct ast_frame *f, void *data)
        ast_pthread_mutex_unlock(&p->lock);
        return 0;
 }
+#endif
 
 static struct mgcp_endpoint *find_endpoint(char *name, int msgid, struct sockaddr_in *sin)
 {
@@ -602,8 +734,18 @@ static struct mgcp_endpoint *find_endpoint(char *name, int msgid, struct sockadd
        ast_pthread_mutex_lock(&gatelock);
        g = gateways;
        while(g) {
-               if (!name || !strcasecmp(g->name, at)) {
-                       /* Found the gateway -- now for the endpoint */
+               if ((!name || !strcasecmp(g->name, at)) && 
+                   (sin || g->addr.sin_addr.s_addr || g->defaddr.sin_addr.s_addr)) {
+                       /* Found the gateway.  If it's dynamic, save it's address -- now for the endpoint */
+                       if (sin && g->dynamic) {
+                               if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) ||
+                                       (g->addr.sin_port != sin->sin_port)) {
+                                       memcpy(&g->addr, sin, sizeof(g->addr));
+                                       memcpy(&g->ourip, myaddrfor(&g->addr.sin_addr), sizeof(g->ourip));
+                                       if (option_verbose > 2)
+                                               ast_verbose(VERBOSE_PREFIX_3 "Registered MGCP gateway '%s' at %s port %d\n", g->name, inet_ntoa(g->addr.sin_addr), ntohs(g->addr.sin_port));
+                               }
+                       }
                        p = g->endpoints;
                        while(p) {
                                if ((name && !strcasecmp(p->name, tmp)) ||
@@ -721,9 +863,11 @@ static void parse(struct mgcp_request *req)
                }
        }
                
-       printf("Verb: '%s', Identifier: '%s', Endpoint: '%s', Version: '%s'\n",
+       if (mgcpdebug) {
+               ast_verbose("Verb: '%s', Identifier: '%s', Endpoint: '%s', Version: '%s'\n",
                req->verb, req->identifier, req->endpoint, req->version);
-       printf("%d headers, %d lines\n", req->headers, req->lines);
+               ast_verbose("%d headers, %d lines\n", req->headers, req->lines);
+       }
        if (*c) 
                ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c);
 }
@@ -732,14 +876,17 @@ static int process_sdp(struct mgcp_endpoint *p, struct mgcp_request *req)
 {
        char *m;
        char *c;
+       char *a;
        char host[258];
        int len;
        int portno;
-       int peercapability;
+       int peercapability, peerNonCodecCapability;
        struct sockaddr_in sin;
        char *codecs;
        struct hostent *hp;
        int codec;
+       int iterator;
+
        /* Get codec and RTP info from SDP */
        m = get_sdp(req, "m");
        c = get_sdp(req, "c");
@@ -764,29 +911,44 @@ static int process_sdp(struct mgcp_endpoint *p, struct mgcp_request *req)
        sin.sin_family = AF_INET;
        memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
        sin.sin_port = htons(portno);
-       if (p->rtp)
-               ast_rtp_set_peer(p->rtp, &sin);
+       ast_rtp_set_peer(p->rtp, &sin);
 #if 0
        printf("Peer RTP is at port %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
 #endif 
-       peercapability = 0;
+       // Scan through the RTP payload types specified in a "m=" line:
+    ast_rtp_pt_clear(p->rtp);
        codecs = m + len;
        while(strlen(codecs)) {
                if (sscanf(codecs, "%d %n", &codec, &len) != 1) {
                        ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
                        return -1;
                }
-#if 0
-               printf("Codec: %d\n", codec);
-#endif         
-               codec = rtp2ast(codec);
-               if (codec  > -1)
-                       peercapability |= codec;
+               ast_rtp_set_m_type(p->rtp, codec);
                codecs += len;
        }
+
+        // Next, scan through each "a=rtpmap:" line, noting each
+        // specified RTP payload type (with corresponding MIME subtype):
+        sdpLineNum_iterator_init(&iterator);
+        while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
+          char* mimeSubtype = strdup(a); // ensures we have enough space
+          if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue;
+          // Note: should really look at the 'freq' and '#chans' params too
+          ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype);
+         free(mimeSubtype);
+        }
+
+        // Now gather all of the codecs that were asked for:
+        ast_rtp_get_current_formats(p->rtp,
+                                &peercapability, &peerNonCodecCapability);
        p->capability = capability & peercapability;
-       printf("Capabilities: us - %d, them - %d, combined - %d\n",
+       if (mgcpdebug) {
+               ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n",
                capability, peercapability, p->capability);
+               ast_verbose("Non-codec capabilities: us - %d, them - %d, combined - %d\n",
+                            nonCodecCapability, peerNonCodecCapability,
+                            p->nonCodecCapability);
+       }
        if (!p->capability) {
                ast_log(LOG_WARNING, "No compatible codecs!\n");
                return -1;
@@ -924,27 +1086,56 @@ static int add_sdp(struct mgcp_request *resp, struct mgcp_endpoint *p, struct as
        if (rtp) {
                ast_rtp_get_peer(rtp, &dest);
        } else {
-               dest.sin_addr = p->parent->ourip;
-               dest.sin_port = sin.sin_port;
+               if (p->tmpdest.sin_addr.s_addr) {
+                       dest.sin_addr = p->tmpdest.sin_addr;
+                       dest.sin_port = p->tmpdest.sin_port;
+                       /* Reset temporary destination */
+                       memset(&p->tmpdest, 0, sizeof(p->tmpdest));
+               } else {
+                       dest.sin_addr = p->parent->ourip;
+                       dest.sin_port = sin.sin_port;
+               }
        }
-       printf("We're at %s port %d\n", inet_ntoa(p->parent->ourip), ntohs(sin.sin_port));      
+       if (mgcpdebug)
+               ast_verbose("We're at %s port %d\n", inet_ntoa(p->parent->ourip), ntohs(sin.sin_port)); 
        snprintf(v, sizeof(v), "v=0\r\n");
        snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), inet_ntoa(dest.sin_addr));
        snprintf(s, sizeof(s), "s=session\r\n");
        snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", inet_ntoa(dest.sin_addr));
        snprintf(t, sizeof(t), "t=0 0\r\n");
        snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port));
-       for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
+       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) {
+                       if (mgcpdebug)
+                               ast_verbose("Answering with capability %d\n", x);
+                       codec = ast_rtp_lookup_code(p->rtp, 1, x);
+                        if (codec > -1) {
                                snprintf(costr, sizeof(costr), " %d", codec);
                                strcat(m, costr);
-                               snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast2rtpn(x));
+                               snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x));
                                strcat(a, costr);
                        }
                }
        }
+       for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
+               if (p->nonCodecCapability & x) {
+                       if (mgcpdebug)
+                               ast_verbose("Answering with non-codec capability %d\n", x);
+                       codec = ast_rtp_lookup_code(p->rtp, 0, x);
+                       if (codec > -1) {
+                               snprintf(costr, sizeof(costr), " %d", codec);
+                               strcat(m, costr);
+                               snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x));
+                               strcat(a, costr);
+                               if (x == AST_RTP_DTMF) {
+                                 /* Indicate we support DTMF...  Not sure about 16, but MSN supports it so dang it, we will too... */
+                                 snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n",
+                                          codec);
+                                 strcat(a, costr);
+                               }
+                       }
+               }
+        }
        strcat(m, "\r\n");
        len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a);
        snprintf(costr, sizeof(costr), "%d", len);
@@ -964,10 +1155,16 @@ static int transmit_modify_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rtp
        char local[256];
        char tmp[80];
        int x;
+       if (!strlen(p->cxident) && rtp) {
+               /* We don't have a CXident yet, store the destination and
+                  wait a bit */
+               ast_rtp_get_peer(rtp, &p->tmpdest);
+               return 0;
+       }
        snprintf(local, sizeof(local), "p:20");
        for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
                if (p->capability & x) {
-                       snprintf(tmp, sizeof(tmp), ", a:%s", ast2rtpn(x));
+                       snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x));
                        strcat(local, tmp);
                }
        }
@@ -976,10 +1173,10 @@ static int transmit_modify_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rtp
        add_header(&resp, "L", local);
        add_header(&resp, "M", "sendrecv");
        add_header(&resp, "X", p->txident);
+       add_header(&resp, "I", p->cxident);
        add_header(&resp, "S", "");
        add_sdp(&resp, p, rtp);
-       p->lastout = oseq;
-       return send_request(p, &resp);
+       return send_request(p, &resp, oseq);
 }
 
 static int transmit_connect_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rtp)
@@ -991,7 +1188,7 @@ static int transmit_connect_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rt
        snprintf(local, sizeof(local), "p:20");
        for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
                if (p->capability & x) {
-                       snprintf(tmp, sizeof(tmp), ", a:%s", ast2rtpn(x));
+                       snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x));
                        strcat(local, tmp);
                }
        }
@@ -1002,8 +1199,7 @@ static int transmit_connect_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rt
        add_header(&resp, "X", p->txident);
        add_header(&resp, "S", "");
        add_sdp(&resp, p, rtp);
-       p->lastout = oseq;
-       return send_request(p, &resp);
+       return send_request(p, &resp, oseq);
 }
 
 static int transmit_notify_request(struct mgcp_endpoint *p, char *tone, int offhook)
@@ -1017,7 +1213,7 @@ static int transmit_notify_request(struct mgcp_endpoint *p, char *tone, int offh
        else
                add_header(&resp, "R", "hd(N)");
        add_header(&resp, "S", tone);
-       return send_request(p, &resp);
+       return send_request(p, &resp, oseq);
 }
 
 static int transmit_notify_request_with_callerid(struct mgcp_endpoint *p, char *tone, int offhook, char *callerid)
@@ -1047,17 +1243,17 @@ static int transmit_notify_request_with_callerid(struct mgcp_endpoint *p, char *
                n = "O";
        if (!l)
                l = "";
-       snprintf(tone2, sizeof(tone2), "%s, ci(%02d/%02d/%02d/%02d,%s,%s)", tone, 
+       snprintf(tone2, sizeof(tone2), "%s,L/ci(%02d/%02d/%02d/%02d,%s,%s)", tone, 
                        tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, l, n);
        strncpy(p->curtone, tone, sizeof(p->curtone) - 1);
        reqprep(&resp, p, "RQNT");
        add_header(&resp, "X", p->txident);
        if (offhook)
-               add_header(&resp, "R", "hu(N), hf(N), D/[0-9#*](N)");
+               add_header(&resp, "R", "hu(N),hf(N),D/[0-9#*](N)");
        else
                add_header(&resp, "R", "hd(N)");
        add_header(&resp, "S", tone2);
-       return send_request(p, &resp);
+       return send_request(p, &resp, oseq);
 }
 static int transmit_connection_del(struct mgcp_endpoint *p)
 {
@@ -1065,11 +1261,28 @@ static int transmit_connection_del(struct mgcp_endpoint *p)
        reqprep(&resp, p, "DLCX");
        add_header(&resp, "C", p->callid);
        add_header(&resp, "I", p->cxident);
-       return send_request(p, &resp);
+       return send_request(p, &resp, oseq);
 }
 
 static void handle_response(struct mgcp_endpoint *p, int result, int ident)
 {
+       struct mgcp_message *cur;
+       if (p->msgs && (p->msgs->seqno == ident)) {
+               ast_log(LOG_DEBUG, "Got response back on tansaction %d\n", ident);
+               cur = p->msgs;
+               p->msgs = p->msgs->next;
+               free(cur);
+               if (p->msgs) {
+                       /* Send next pending message if appropriate */
+                       p->messagepending = 1;
+                       p->lastout = p->msgs->seqno;
+                       __mgcp_xmit(p, p->msgs->buf, p->msgs->len);
+                       /* XXX Should schedule retransmission XXX */
+               } else
+                       p->messagepending = 0;
+       } else {
+               ast_log(LOG_NOTICE, "Got response back on transaction %d we aren't sending? (current = %d)\n", ident, p->msgs ? p->msgs->seqno : -1);
+       }
        if ((result >= 400) && (result <= 499)) {
                ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s\n", result, p->name, p->parent->name);
                if (p->owner)
@@ -1081,9 +1294,15 @@ static void start_rtp(struct mgcp_endpoint *p)
 {
                ast_pthread_mutex_lock(&p->lock);
                /* Allocate the RTP now */
-               p->rtp = ast_rtp_new(sched, io);
+               p->rtp = ast_rtp_new(NULL, NULL);
+               if (p->rtp && p->owner)
+                       p->owner->fds[0] = ast_rtp_fd(p->rtp);
+               if (p->rtp)
+                       ast_rtp_setnat(p->rtp, p->nat);
+#if 0
                ast_rtp_set_callback(p->rtp, rtpready);
                ast_rtp_set_data(p->rtp, p);
+#endif         
                /* Make a call*ID */
                snprintf(p->callid, sizeof(p->callid), "%08x%s", rand(), p->txident);
                /* Transmit the connection create */
@@ -1140,14 +1359,17 @@ static int handle_request(struct mgcp_endpoint *p, struct mgcp_request *req, str
        struct ast_channel *c;
        pthread_t t;
        struct ast_frame f = { 0, };
-       printf("Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name);
+       if (mgcpdebug)
+               ast_verbose("Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name);
        /* Clear out potential response */
        if (!strcasecmp(req->verb, "RSIP")) {
+               dump_queue(p);
                if (option_verbose > 2)
                        ast_verbose(VERBOSE_PREFIX_3 "Resetting interface %s@%s\n", p->name, p->parent->name);
                if (p->owner)
                        ast_softhangup(p->owner, AST_SOFTHANGUP_DEV);
                transmit_response(p, "200", req, "OK");
+               transmit_notify_request(p, "", 0);
        } else if (!strcasecmp(req->verb, "NTFY")) {
                /* Acknowledge and be sure we keep looking for the same things */
                transmit_response(p, "200", req, "OK");
@@ -1189,6 +1411,7 @@ static int handle_request(struct mgcp_endpoint *p, struct mgcp_request *req, str
                                p->alreadygone = 1;
                                ast_queue_hangup(p->owner, 1);
                        }
+                       transmit_notify_request(p, "", 0);
                } else if ((strlen(ev) == 1) && 
                                        (((ev[0] >= '0') && (ev[0] <= '9')) ||
                                         ((ev[0] >= 'A') && (ev[0] <= 'D')) ||
@@ -1230,7 +1453,8 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore)
        }
        req.data[res] = '\0';
        req.len = res;
-       printf("MGCP read: \n%s\n", req.data);
+       if (mgcpdebug)
+               ast_verbose("MGCP read: \n%s\nfrom %s:%d", req.data, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
        parse(&req);
        if (req.headers < 1) {
                /* Must have at least one header */
@@ -1247,10 +1471,20 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore)
                p = find_endpoint(NULL, ident, &sin);
                if (p) {
                        handle_response(p, result, ident);
-                       if ((c = get_header(&req, "I")))
-                               strncpy(p->cxident, c, sizeof(p->cxident) - 1);
-                       if (req.lines)
-                               process_sdp(p, &req);
+                       if ((c = get_header(&req, "I"))) {
+                               if (strlen(c)) {
+                                       strncpy(p->cxident, c, sizeof(p->cxident) - 1);
+                                       if (p->tmpdest.sin_addr.s_addr) {
+                                               transmit_modify_with_sdp(p, NULL);
+                                       }
+                               }
+                       }
+                       if (req.lines) {
+                               if (!p->rtp) 
+                                       start_rtp(p);
+                               if (p->rtp)
+                                       process_sdp(p, &req);
+                       }
                }
        } else {
                if (!req.endpoint || !strlen(req.endpoint) || 
@@ -1373,6 +1607,7 @@ static struct ast_channel *mgcp_request(char *type, int format, void *data)
                ast_log(LOG_WARNING, "Unable to find MGCP endpoint '%s'\n", tmp);
                return NULL;
        }
+       
        /* Must be busy */
        if (p->owner)
                return NULL;
@@ -1390,14 +1625,38 @@ struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
        char context[AST_MAX_EXTENSION] = "default";
        char language[80] = "";
        char callerid[AST_MAX_EXTENSION] = "";
+       int inbanddtmf = 0;
+       int nat = 0;
 
        gw = malloc(sizeof(struct mgcp_gateway));
        if (gw) {
                memset(gw, 0, sizeof(struct mgcp_gateway));
+               gw->expire = -1;
                strncpy(gw->name, cat, sizeof(gw->name) - 1);
                while(v) {
                        if (!strcasecmp(v->name, "host")) {
-                               if (ast_get_ip(&gw->addr, v->value)) {
+                               if (!strcasecmp(v->value, "dynamic")) {
+                                       /* They'll register with us */
+                                       gw->dynamic = 1;
+                                       memset(&gw->addr.sin_addr, 0, 4);
+                                       if (gw->addr.sin_port) {
+                                               /* If we've already got a port, make it the default rather than absolute */
+                                               gw->defaddr.sin_port = gw->addr.sin_port;
+                                               gw->addr.sin_port = 0;
+                                       }
+                               } else {
+                                       /* Non-dynamic.  Make sure we become that way if we're not */
+                                       if (gw->expire > -1)
+                                               ast_sched_del(sched, gw->expire);
+                                       gw->expire = -1;
+                                       gw->dynamic = 0;
+                                       if (ast_get_ip(&gw->addr, v->value)) {
+                                               free(gw);
+                                               return NULL;
+                                       }
+                               }
+                       } else if (!strcasecmp(v->name, "defaultip")) {
+                               if (ast_get_ip(&gw->defaddr, v->value)) {
                                        free(gw);
                                        return NULL;
                                }
@@ -1408,6 +1667,10 @@ struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                                gw->addr.sin_port = htons(atoi(v->value));
                        } else if (!strcasecmp(v->name, "context")) {
                                strncpy(context, v->value, sizeof(context) - 1);
+                       } else if (!strcasecmp(v->name, "inbanddtmf")) {
+                               inbanddtmf = atoi(v->value);
+                       } else if (!strcasecmp(v->name, "nat")) {
+                               nat = ast_true(v->value);
                        } else if (!strcasecmp(v->name, "callerid")) {
                                if (!strcasecmp(v->value, "asreceived"))
                                        strcpy(callerid, "");
@@ -1427,6 +1690,8 @@ struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                                        strncpy(e->language, language, sizeof(e->language) - 1);
                                        e->capability = capability;
                                        e->parent = gw;
+                                       e->dtmfinband = inbanddtmf;
+                                       e->nat = nat;
                                        strncpy(e->name, v->value, sizeof(e->name) - 1);
                                        if (!strcasecmp(v->name, "trunk"))
                                                e->type = TYPE_TRUNK;
@@ -1441,17 +1706,78 @@ struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                }
                
        }
-       if (!ntohl(gw->addr.sin_addr.s_addr)) {
-               ast_log(LOG_WARNING, "Gateway '%s' lacks IP address\n", gw->name);
+
+       if (!ntohl(gw->addr.sin_addr.s_addr) && !gw->dynamic) {
+               ast_log(LOG_WARNING, "Gateway '%s' lacks IP address and isn't dynamic\n", gw->name);
                free(gw);
-               gw = NULL;
-       } else if (!ntohs(gw->addr.sin_port)) {
+               return NULL;
+       }
+       if (gw->defaddr.sin_addr.s_addr && !ntohs(gw->defaddr.sin_port)) 
+               gw->defaddr.sin_port = htons(DEFAULT_MGCP_PORT);
+       if (gw->addr.sin_addr.s_addr && !ntohs(gw->addr.sin_port))
                gw->addr.sin_port = htons(DEFAULT_MGCP_PORT);
+       if (gw->addr.sin_addr.s_addr)
                memcpy(&gw->ourip, myaddrfor(&gw->addr.sin_addr), sizeof(gw->ourip));
-       }
        return gw;
 }
 
+static struct ast_rtp *mgcp_get_rtp_peer(struct ast_channel *chan)
+{
+       struct mgcp_endpoint *p;
+       p = chan->pvt->pvt;
+       if (p && p->rtp)
+               return p->rtp;
+       return NULL;
+}
+
+static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
+{
+       struct mgcp_endpoint *p;
+       p = chan->pvt->pvt;
+       if (p) {
+               transmit_modify_with_sdp(p, rtp);
+               return 0;
+       }
+       return -1;
+}
+
+static struct ast_rtp_protocol mgcp_rtp = {
+       get_rtp_info: mgcp_get_rtp_peer,
+       set_rtp_peer: mgcp_set_rtp_peer,
+};
+
+static int mgcp_do_debug(int fd, int argc, char *argv[])
+{
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       mgcpdebug = 1;
+       ast_cli(fd, "MGCP Debugging Enabled\n");
+       return RESULT_SUCCESS;
+}
+
+static int mgcp_no_debug(int fd, int argc, char *argv[])
+{
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       mgcpdebug = 0;
+       ast_cli(fd, "MGCP Debugging Disabled\n");
+       return RESULT_SUCCESS;
+}
+
+static char debug_usage[] = 
+"Usage: mgcp debug\n"
+"       Enables dumping of MGCP packets for debugging purposes\n";
+
+static char no_debug_usage[] = 
+"Usage: mgcp no debug\n"
+"       Disables dumping of MGCP packets for debugging purposes\n";
+
+static struct ast_cli_entry  cli_debug =
+       { { "mgcp", "debug", NULL }, mgcp_do_debug, "Enable MGCP debugging", debug_usage };
+static struct ast_cli_entry  cli_no_debug =
+       { { "mgcp", "no", "debug", NULL }, mgcp_no_debug, "Disable MGCP debugging", no_debug_usage };
+
+
 int load_module()
 {
        struct ast_config *cfg;
@@ -1559,7 +1885,11 @@ int load_module()
                ast_destroy(cfg);
                return -1;
        }
+       mgcp_rtp.type = type;
+       ast_rtp_proto_register(&mgcp_rtp);
        ast_cli_register(&cli_show_endpoints);
+       ast_cli_register(&cli_debug);
+       ast_cli_register(&cli_no_debug);
        /* And start the monitor for the first time */
        restart_monitor();
        return 0;
@@ -1567,8 +1897,8 @@ int load_module()
 
 int unload_module()
 {
-       struct mgcp_endpoint *p, *pl;
 #if 0
+       struct mgcp_endpoint *p, *pl;
        /* First, take us out of the channel loop */
        ast_channel_unregister(type);
        if (!ast_pthread_mutex_lock(&gatelock)) {