Add MGCP audit
[asterisk/asterisk.git] / channels / chan_mgcp.c
index 0b7627d..96c67ec 100755 (executable)
@@ -109,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
 
@@ -141,6 +149,8 @@ struct mgcp_endpoint {
        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;
 };
@@ -169,6 +179,7 @@ static int transmit_response(struct mgcp_endpoint *p, char *msg, struct mgcp_req
 static int transmit_notify_request(struct mgcp_endpoint *p, char *tone, int offhook);
 static int transmit_connection_del(struct mgcp_endpoint *p);
 static int transmit_notify_request_with_callerid(struct mgcp_endpoint *p, char *tone, int offhook, char *callerid);
+static int transmit_audit_endpoint(struct mgcp_endpoint *p);
 
 static int __mgcp_xmit(struct mgcp_endpoint *p, char *data, int len)
 {
@@ -194,12 +205,52 @@ static int send_response(struct mgcp_endpoint *p, struct mgcp_request *req)
        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;
        if (mgcpdebug)
-               ast_verbose("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);
+               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;
 }
 
@@ -217,7 +268,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 {
@@ -392,6 +443,63 @@ static char show_endpoints_usage[] =
 static struct ast_cli_entry  cli_show_endpoints = 
        { { "mgcp", "show", "endpoints", NULL }, mgcp_show_endpoints, "Show defined MGCP endpoints", show_endpoints_usage };
 
+static int mgcp_audit_endpoint(int fd, int argc, char *argv[])
+{
+       struct mgcp_gateway  *g;
+       struct mgcp_endpoint *e;
+       int found = 0;
+    char *ename,*gname;
+       if (!mgcpdebug) {
+               return RESULT_SHOWUSAGE;
+    }
+       if (argc != 4) 
+               return RESULT_SHOWUSAGE;
+    /* split the name into parts by null */
+    ename = argv[3];
+    gname = ename;
+    while (*gname) {
+        if (*gname == '@') {
+            *gname = 0;
+            gname++;
+            break;
+        }
+        gname++;
+    }
+
+       ast_pthread_mutex_lock(&gatelock);
+       g = gateways;
+       while(g) {
+        if (!strcasecmp(g->name, gname)) {
+            e = g->endpoints;
+            while(e) {
+                if (!strcasecmp(e->name, ename)) {
+                    found = 1;
+                    transmit_audit_endpoint(e);
+                    break;
+                }
+                e = e->next;
+            }
+            if (found) {
+                break;
+            }
+        }
+        g = g->next;
+       }
+    if (!found) {
+        ast_cli(fd, "   << Could not find endpoint >>     ");
+    }
+       ast_pthread_mutex_unlock(&gatelock);
+       return RESULT_SUCCESS;
+}
+
+static char audit_endpoint_usage[] = 
+"Usage: mgcp audit endpoint <endpointid>\n"
+"       List the capabilities of an endpoint in the MGCP (Media Gateawy Control Protocol) subsystem.\n"
+"       mgcp debug MUST be on to see the results of this command.\n";
+
+static struct ast_cli_entry  cli_audit_endpoint = 
+       { { "mgcp", "audit", "endpoint", NULL }, mgcp_audit_endpoint, "Audit specified MGCP endpoint", audit_endpoint_usage };
+
 static int mgcp_answer(struct ast_channel *ast)
 {
        int res = 0;
@@ -861,8 +969,7 @@ 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 
@@ -1127,8 +1234,7 @@ static int transmit_modify_with_sdp(struct mgcp_endpoint *p, struct ast_rtp *rtp
        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)
@@ -1151,8 +1257,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)
@@ -1166,7 +1271,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)
@@ -1176,10 +1281,10 @@ static int transmit_notify_request_with_callerid(struct mgcp_endpoint *p, char *
        char tone2[256];
        char *l, *n;
        time_t t;
-       struct tm *tm;
+       struct tm tm;
        
        time(&t);
-       tm = localtime(&t);
+       localtime_r(&t,&tm);
        if (callerid)
                strncpy(cid, callerid, sizeof(cid) - 1);
        else
@@ -1196,29 +1301,55 @@ 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, 
-                       tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, l, n);
+       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", "L/hu(N),L/hf(N),D/[0-9#*](N)");
        else
-               add_header(&resp, "R", "hd(N)");
+               add_header(&resp, "R", "L/hd(N)");
        add_header(&resp, "S", tone2);
-       return send_request(p, &resp);
+       return send_request(p, &resp, oseq);
+}
+
+static int transmit_audit_endpoint(struct mgcp_endpoint *p)
+{
+       struct mgcp_request resp;
+       reqprep(&resp, p, "AUEP");
+       add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,VS,E,MD");
+       return send_request(p, &resp, oseq);
 }
+
 static int transmit_connection_del(struct mgcp_endpoint *p)
 {
        struct mgcp_request resp;
        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)
@@ -1299,11 +1430,13 @@ static int handle_request(struct mgcp_endpoint *p, struct mgcp_request *req, str
                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");
@@ -1345,6 +1478,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')) ||
@@ -1412,8 +1546,12 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore)
                                        }
                                }
                        }
-                       if (req.lines)
-                               process_sdp(p, &req);
+                       if (req.lines) {
+                               if (!p->rtp) 
+                                       start_rtp(p);
+                               if (p->rtp)
+                                       process_sdp(p, &req);
+                       }
                }
        } else {
                if (!req.endpoint || !strlen(req.endpoint) || 
@@ -1817,6 +1955,7 @@ int load_module()
        mgcp_rtp.type = type;
        ast_rtp_proto_register(&mgcp_rtp);
        ast_cli_register(&cli_show_endpoints);
+       ast_cli_register(&cli_audit_endpoint);
        ast_cli_register(&cli_debug);
        ast_cli_register(&cli_no_debug);
        /* And start the monitor for the first time */
@@ -1826,8 +1965,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)) {