Version 0.1.8 from FTP
[asterisk/asterisk.git] / channels / chan_iax.c
index f3e7d33..d9dd1ad 100755 (executable)
@@ -125,7 +125,17 @@ struct iax_peer {
        char username[80];
        char secret[80];
        struct sockaddr_in addr;
+       int formats;
        struct in_addr mask;
+
+       /* Dynamic Registration fields */
+       int dynamic;
+       struct sockaddr_in defaddr;
+       char regsecret[80];
+       char methods[80];
+       struct timeval nexpire;
+       int expire;
+       struct iax_ha *ha;
        struct iax_peer *next;
 };
        
@@ -326,18 +336,24 @@ static int get_timelen(struct ast_frame *f)
        int timelen=0;
        switch(f->subclass) {
        case AST_FORMAT_G723_1:
-               timelen = 30;
+               timelen = 30 /* XXX Not necessarily true XXX */;
                break;
        case AST_FORMAT_GSM:
-               timelen = 20;
+               timelen = 20 * (f->datalen / 20);
                break;
        case AST_FORMAT_SLINEAR:
-               timelen = f->datalen / 8;
+               timelen = f->datalen / 16;
                break;
        case AST_FORMAT_LPC10:
                timelen = 22;
                timelen += ((char *)(f->data))[7] & 0x1;
                break;
+       case AST_FORMAT_ULAW:
+               timelen = f->datalen / 8;
+               break;
+       case AST_FORMAT_ADPCM:
+               timelen = f->datalen / 4;
+               break;
        default:
                ast_log(LOG_WARNING, "Don't know how to calculate timelen on %d packets\n", f->subclass);
        }
@@ -451,8 +467,9 @@ static int do_deliver(void *data)
                                ts = calc_timestamp(iaxs[fr->callno], 0);
                                iaxs[fr->callno]->lag = ts - fr->ts;
                        }
-               } else
+               } else {
                        ast_fr_fdwrite(iaxs[fr->callno]->pipe[1], fr->f);
+               }
        }
        /* Free the packet */
        ast_frfree(fr->f);
@@ -668,8 +685,8 @@ static int attempt_transmit(void *data)
                        /* Attempt transmission */
                        send_packet(f);
                        f->retries++;
-                       /* Try again later after 2 times as long */
-                       f->retrytime *= 2;
+                       /* Try again later after 10 times as long */
+                       f->retrytime *= 10;
                        if (f->retrytime > MAX_RETRY_TIME)
                                f->retrytime = MAX_RETRY_TIME;
                        ast_sched_add(sched, f->retrytime, attempt_transmit, f);
@@ -854,6 +871,13 @@ static int iax_digit(struct ast_channel *c, char digit)
        return send_command(c->pvt->pvt, AST_FRAME_DTMF, digit, 0, NULL, 0, -1);
 }
 
+static int iax_sendtext(struct ast_channel *c, char *text)
+{
+       
+       return send_command(c->pvt->pvt, AST_FRAME_TEXT,
+               0, 0, text, strlen(text) + 1, -1);
+}
+
 static int create_addr(struct sockaddr_in *sin, char *peer)
 {
        struct hostent *hp;
@@ -891,14 +915,15 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
        char *hname;
        char requeststr[256] = "";
        char myrdest [5] = "s";
+       char *portno = NULL;
        if ((c->state != AST_STATE_DOWN) && (c->state != AST_STATE_RESERVED)) {
                ast_log(LOG_WARNING, "Line is already in use (%s)?\n", c->name);
                return -1;
        }
        strncpy(host, dest, sizeof(host));
-       strtok(host, ":");
+       strtok(host, "/");
        /* If no destination extension specified, use 's' */
-       rdest = strtok(NULL, ":");
+       rdest = strtok(NULL, "/");
        if (!rdest) 
                rdest = myrdest;
        strtok(rdest, "@");
@@ -912,10 +937,17 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
        } else {
                hname = host;
        }
+       if (strtok(hname, ":")) {
+               strtok(hname, ":");
+               portno = strtok(hname, ":");
+       }
        if (create_addr(&sin, hname)) {
                ast_log(LOG_WARNING, "No address associated with '%s'\n", hname);
                return -1;
        }
+       if (portno) {
+               sin.sin_port = htons(atoi(portno));
+       }
        /* Now we build our request string */
 #define MYSNPRINTF snprintf(requeststr + strlen(requeststr), sizeof(requeststr) - strlen(requeststr), 
        MYSNPRINTF "exten=%s;", rdest);
@@ -927,14 +959,14 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout)
                MYSNPRINTF "context=%s;", rcontext);
        if (username)
                MYSNPRINTF "username=%s;", username);
-       MYSNPRINTF "formats=%d;", c->format);
+       MYSNPRINTF "formats=%d;", c->nativeformats);
        MYSNPRINTF "version=%d;", AST_IAX_PROTO_VERSION);
        /* Trim the trailing ";" */
        if (strlen(requeststr))
                requeststr[strlen(requeststr) - 1] = '\0';
        /* Transmit the string in a "NEW" request */
        if (option_verbose > 2)
-       ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
+               ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
        send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_IAX,
                AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1);
        c->state = AST_STATE_RINGING;
@@ -1020,9 +1052,10 @@ static struct ast_channel *ast_iax_new(struct chan_iax_pvt *i, int state)
                tmp->type = type;
                tmp->fd = i->pipe[0];
                /* We can support any format by default, until we get restricted */
-               tmp->format = iax_capability;
+               tmp->nativeformats = iax_capability;
                tmp->pvt->pvt = i;
                tmp->pvt->send_digit = iax_digit;
+               tmp->pvt->send_text = iax_sendtext;
                tmp->pvt->call = iax_call;
                tmp->pvt->hangup = iax_hangup;
                tmp->pvt->answer = iax_answer;
@@ -1312,6 +1345,27 @@ static int apply_ha(struct iax_ha *ha, struct sockaddr_in *sin)
        return res;
 }
 
+static int iax_getformats(int callno, char *orequest)
+{
+       char *var, *value;
+       char request[256];
+       strncpy(request, orequest, sizeof(request));
+       var = strtok(request, ";");
+       while(var) {
+               value = strchr(var, '=');
+               if (value) {
+                       *value='\0';
+                       value++;
+                       if (!strcmp(var, "formats")) {
+                               iaxs[callno]->peerformats = atoi(value);
+                       } else 
+                               ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+               }
+               var = strtok(NULL, ";");
+       }
+       return 0;
+}
+
 static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int requestl)
 {
        /* Start pessimistic */
@@ -1662,11 +1716,13 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                if (!strlen(iaxs[fr.callno]->secret)) {
                                        /* No authentication required, let them in */
                                        send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
+                                       if (option_verbose > 2) 
+                                               ast_verbose(VERBOSE_PREFIX_3 "Accepting unauthenticated call from %s, formats = %d\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformats);
                                        iaxs[fr.callno]->state |= IAX_STATE_STARTED;
                                        if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
                                                iax_destroy(fr.callno);
                                        else
-                                               c->format = iaxs[fr.callno]->peerformats;
+                                               c->nativeformats = iaxs[fr.callno]->peerformats;
                                        break;
                                }
                                authenticate_request(iaxs[fr.callno]);
@@ -1679,15 +1735,38 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                iax_destroy(fr.callno);
                                break;
                        case AST_IAX_COMMAND_REJECT:
-                               ((char *)f.data)[f.datalen] = '\0';
+                               if (f.data)
+                                       ((char *)f.data)[f.datalen] = '\0';
                                ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), f.data);
                                iaxs[fr.callno]->error = EPERM;
                                iax_destroy(fr.callno);
                                break;
                        case AST_IAX_COMMAND_ACCEPT:
+                               if (f.data) {
+                                       ((char *)f.data)[f.datalen]='\0';
+                                       iax_getformats(fr.callno, (char *)f.data);
+                               } else {
+                                       iaxs[fr.callno]->peerformats = iax_capability;
+                               }
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Call accepted by %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
                                iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+                               if (iaxs[fr.callno]->owner) {
+                                       /* Switch us to use a compatible format */
+                                       iaxs[fr.callno]->owner->nativeformats &= iaxs[fr.callno]->peerformats;
+
+                                       if (!iaxs[fr.callno]->owner->nativeformats) 
+                                               iaxs[fr.callno]->owner->nativeformats = iaxs[fr.callno]->peerformats & iax_capability;
+                                       if (!iaxs[fr.callno]->owner->nativeformats) {
+                                               ast_log(LOG_WARNING, "Unable to negotiate a common format with the peer.");
+                                               iaxs[fr.callno]->error = EBADE;
+                                               iax_destroy(fr.callno);
+                                       } else {
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Format for call is %d\n", iaxs[fr.callno]->owner->nativeformats);
+                                       }
+                                               
+                               }
                                break;
                        case AST_IAX_COMMAND_PING:
                                /* Send back a pong packet with the original timestamp */
@@ -1730,13 +1809,15 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                                        iax_destroy(fr.callno);
                                        break;
                                }
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s, formats = %dn", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformats);
                                /* Authentication is fine, go ahead */
                                send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
                                iaxs[fr.callno]->state |= IAX_STATE_STARTED;
                                if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
                                        iax_destroy(fr.callno);
                                else
-                                       c->format = iaxs[fr.callno]->peerformats;
+                                       c->nativeformats = iaxs[fr.callno]->peerformats;
                                break;
                        case AST_IAX_COMMAND_INVAL:
                                iaxs[fr.callno]->error = ENOTCONN;
@@ -1794,7 +1875,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
                iaxs[fr.callno]->last = fr.ts;
                fr.outoforder = 0;
        } else {
-               ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr.ts, iaxs[fr.callno]->last);
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr.ts, iaxs[fr.callno]->last);
                fr.outoforder = -1;
        }
        schedule_delivery(iaxfrdup2(&fr, 0));
@@ -1826,19 +1908,21 @@ static void free_context(struct iax_context *con)
 static struct ast_channel *iax_request(char *type, int format, void *data)
 {
        int callno;
+       int res;
+       int fmt, native;
        struct sockaddr_in sin;
        char s[256];
        char *st;
        struct ast_channel *c;
        strncpy(s, (char *)data, sizeof(s));
-       strtok(s, ":");
+       strtok(s, "/");
        strtok(s, "@");
        st = strtok(NULL, "@");
        if (!st)
                st = s;
        /* Populate our address from the given */
        if (create_addr(&sin, st)) {
-               ast_log(LOG_WARNING, "Unable to assign address\n");
+               ast_log(LOG_WARNING, "Unable to assign address for %s\n", st);
                return NULL;
        }
        pthread_mutex_lock(&iaxs_lock);
@@ -1850,10 +1934,19 @@ static struct ast_channel *iax_request(char *type, int format, void *data)
        c = ast_iax_new(iaxs[callno], AST_STATE_DOWN);
        if (c) {
                /* Choose a format we can live with */
-               if (c->format & format)
-                       c->format &= format;
-               else 
-                       c->format = ast_translator_best_choice(format, c->format);
+               if (c->nativeformats & format)
+                       c->nativeformats &= format;
+               else {
+                       native = c->nativeformats;
+                       fmt = format;
+                       res = ast_translator_best_choice(&fmt, &native);
+                       if (res < 0) {
+                               ast_log(LOG_WARNING, "Unable to create translator path for %d to %d on %s\n", c->nativeformats, fmt, c->name);
+                               ast_hangup(c);
+                               return NULL;
+                       }
+                       c->nativeformats = native;
+               }
        }
        pthread_mutex_unlock(&iaxs_lock);
        return c;
@@ -1976,16 +2069,23 @@ static struct iax_peer *build_peer(char *name, struct ast_variable *v)
                        if (!strcasecmp(v->name, "secret")) 
                                strncpy(peer->secret, v->value, sizeof(peer->secret));
                        else if (!strcasecmp(v->name, "host")) {
-                               hp = gethostbyname(v->value);
-                               if (hp) {
-                                       memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+                               if (!strcasecmp(v->value, "dynamic")) {
+                                       /* They'll register with us */
+                                       peer->dynamic = 1;
+                                       memset(&peer->addr.sin_addr, 0, 4);
                                } else {
-                                       ast_log(LOG_WARNING, "Unable to lookup '%s'\n", v->value);
-                                       free(peer);
-                                       return NULL;
+                                       peer->dynamic = 0;
+                                       hp = gethostbyname(v->value);
+                                       if (hp) {
+                                               memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+                                       } else {
+                                               ast_log(LOG_WARNING, "Unable to lookup '%s'\n", v->value);
+                                               free(peer);
+                                               return NULL;
+                                       }
+                                       if (!maskfound)
+                                               inet_aton("255.255.255.255", &peer->mask);
                                }
-                               if (!maskfound)
-                                       inet_aton("255.255.255.255", &peer->mask);
                        }
                        else if (!strcasecmp(v->name, "mask")) {
                                maskfound++;
@@ -2236,3 +2336,7 @@ int usecount()
        return res;
 }
 
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}