Fri Jun 19 12:09:46 2009

Asterisk developer's documentation


iax2-parser.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Implementation of Inter-Asterisk eXchange Protocol, v 2
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 143033 $")
00029 
00030 #include <sys/socket.h>
00031 #include <netinet/in.h>
00032 #include <arpa/inet.h>
00033 
00034 #include "asterisk/frame.h"
00035 #include "asterisk/utils.h"
00036 #include "asterisk/unaligned.h"
00037 #include "asterisk/config.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/threadstorage.h"
00040 
00041 #include "iax2.h"
00042 #include "iax2-parser.h"
00043 #include "iax2-provision.h"
00044 
00045 static int frames = 0;
00046 static int iframes = 0;
00047 static int oframes = 0;
00048 
00049 #if !defined(LOW_MEMORY)
00050 static void frame_cache_cleanup(void *data);
00051 
00052 /*! \brief A per-thread cache of iax_frame structures */
00053 AST_THREADSTORAGE_CUSTOM(frame_cache, NULL, frame_cache_cleanup);
00054 
00055 /*! \brief This is just so iax_frames, a list head struct for holding a list of
00056  *  iax_frame structures, is defined. */
00057 AST_LIST_HEAD_NOLOCK(iax_frame_list, iax_frame);
00058 
00059 struct iax_frames {
00060    struct iax_frame_list list;
00061    size_t size;
00062 };
00063 
00064 #define FRAME_CACHE_MAX_SIZE  20
00065 #endif
00066 
00067 static void internaloutput(const char *str)
00068 {
00069    fputs(str, stdout);
00070 }
00071 
00072 static void internalerror(const char *str)
00073 {
00074    fprintf(stderr, "WARNING: %s", str);
00075 }
00076 
00077 static void (*outputf)(const char *str) = internaloutput;
00078 static void (*errorf)(const char *str) = internalerror;
00079 
00080 static void dump_addr(char *output, int maxlen, void *value, int len)
00081 {
00082    struct sockaddr_in sin;
00083    if (len == (int)sizeof(sin)) {
00084       memcpy(&sin, value, len);
00085       snprintf(output, maxlen, "IPV4 %s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
00086    } else {
00087       ast_copy_string(output, "Invalid Address", maxlen);
00088    }
00089 }
00090 
00091 static void dump_string_hex(char *output, int maxlen, void *value, int len)
00092 {
00093    int i = 0;
00094 
00095    while (len-- && (i + 1) * 4 < maxlen) {
00096       sprintf(output + (4 * i), "\\x%2.2x", *((unsigned char *)value + i));
00097       i++;
00098    }
00099 }
00100 
00101 static void dump_string(char *output, int maxlen, void *value, int len)
00102 {
00103    maxlen--;
00104    if (maxlen > len)
00105       maxlen = len;
00106    strncpy(output, value, maxlen);
00107    output[maxlen] = '\0';
00108 }
00109 
00110 static void dump_prefs(char *output, int maxlen, void *value, int len)
00111 {
00112    struct ast_codec_pref pref;
00113    int total_len = 0;
00114 
00115    maxlen--;
00116    total_len = maxlen;
00117 
00118    if (maxlen > len)
00119       maxlen = len;
00120 
00121    strncpy(output, value, maxlen);
00122    output[maxlen] = '\0';
00123    
00124    ast_codec_pref_convert(&pref, output, total_len, 0);
00125    memset(output,0,total_len);
00126    ast_codec_pref_string(&pref, output, total_len);
00127 }
00128 
00129 static void dump_int(char *output, int maxlen, void *value, int len)
00130 {
00131    if (len == (int)sizeof(unsigned int))
00132       snprintf(output, maxlen, "%lu", (unsigned long)ntohl(get_unaligned_uint32(value)));
00133    else
00134       ast_copy_string(output, "Invalid INT", maxlen); 
00135 }
00136 
00137 static void dump_short(char *output, int maxlen, void *value, int len)
00138 {
00139    if (len == (int)sizeof(unsigned short))
00140       snprintf(output, maxlen, "%d", ntohs(get_unaligned_uint16(value)));
00141    else
00142       ast_copy_string(output, "Invalid SHORT", maxlen);
00143 }
00144 
00145 static void dump_byte(char *output, int maxlen, void *value, int len)
00146 {
00147    if (len == (int)sizeof(unsigned char))
00148       snprintf(output, maxlen, "%d", *((unsigned char *)value));
00149    else
00150       ast_copy_string(output, "Invalid BYTE", maxlen);
00151 }
00152 
00153 static void dump_datetime(char *output, int maxlen, void *value, int len)
00154 {
00155    struct ast_tm tm;
00156    unsigned long val = (unsigned long) ntohl(get_unaligned_uint32(value));
00157    if (len == (int)sizeof(unsigned int)) {
00158       tm.tm_sec  = (val & 0x1f) << 1;
00159       tm.tm_min  = (val >> 5) & 0x3f;
00160       tm.tm_hour = (val >> 11) & 0x1f;
00161       tm.tm_mday = (val >> 16) & 0x1f;
00162       tm.tm_mon  = ((val >> 21) & 0x0f) - 1;
00163       tm.tm_year = ((val >> 25) & 0x7f) + 100;
00164       ast_strftime(output, maxlen, "%Y-%m-%d  %T", &tm); 
00165    } else
00166       ast_copy_string(output, "Invalid DATETIME format!", maxlen);
00167 }
00168 
00169 static void dump_ipaddr(char *output, int maxlen, void *value, int len)
00170 {
00171    struct sockaddr_in sin;
00172    if (len == (int)sizeof(unsigned int)) {
00173       memcpy(&sin.sin_addr, value, len);
00174       snprintf(output, maxlen, "%s", ast_inet_ntoa(sin.sin_addr));
00175    } else
00176       ast_copy_string(output, "Invalid IPADDR", maxlen);
00177 }
00178 
00179 
00180 static void dump_prov_flags(char *output, int maxlen, void *value, int len)
00181 {
00182    char buf[256] = "";
00183    if (len == (int)sizeof(unsigned int))
00184       snprintf(output, maxlen, "%lu (%s)", (unsigned long)ntohl(get_unaligned_uint32(value)),
00185          iax_provflags2str(buf, sizeof(buf), ntohl(get_unaligned_uint32(value))));
00186    else
00187       ast_copy_string(output, "Invalid INT", maxlen);
00188 }
00189 
00190 static void dump_samprate(char *output, int maxlen, void *value, int len)
00191 {
00192    char tmp[256]="";
00193    int sr;
00194    if (len == (int)sizeof(unsigned short)) {
00195       sr = ntohs(*((unsigned short *)value));
00196       if (sr & IAX_RATE_8KHZ)
00197          strcat(tmp, ",8khz");
00198       if (sr & IAX_RATE_11KHZ)
00199          strcat(tmp, ",11.025khz");
00200       if (sr & IAX_RATE_16KHZ)
00201          strcat(tmp, ",16khz");
00202       if (sr & IAX_RATE_22KHZ)
00203          strcat(tmp, ",22.05khz");
00204       if (sr & IAX_RATE_44KHZ)
00205          strcat(tmp, ",44.1khz");
00206       if (sr & IAX_RATE_48KHZ)
00207          strcat(tmp, ",48khz");
00208       if (strlen(tmp))
00209          ast_copy_string(output, &tmp[1], maxlen);
00210       else
00211          ast_copy_string(output, "None Specified!\n", maxlen);
00212    } else
00213       ast_copy_string(output, "Invalid SHORT", maxlen);
00214 
00215 }
00216 
00217 static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len);
00218 static void dump_prov(char *output, int maxlen, void *value, int len)
00219 {
00220    dump_prov_ies(output, maxlen, value, len);
00221 }
00222 
00223 static struct iax2_ie {
00224    int ie;
00225    char *name;
00226    void (*dump)(char *output, int maxlen, void *value, int len);
00227 } infoelts[] = {
00228    { IAX_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
00229    { IAX_IE_CALLING_NUMBER, "CALLING NUMBER", dump_string },
00230    { IAX_IE_CALLING_ANI, "ANI", dump_string },
00231    { IAX_IE_CALLING_NAME, "CALLING NAME", dump_string },
00232    { IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
00233    { IAX_IE_USERNAME, "USERNAME", dump_string },
00234    { IAX_IE_PASSWORD, "PASSWORD", dump_string },
00235    { IAX_IE_CAPABILITY, "CAPABILITY", dump_int },
00236    { IAX_IE_FORMAT, "FORMAT", dump_int },
00237    { IAX_IE_LANGUAGE, "LANGUAGE", dump_string },
00238    { IAX_IE_VERSION, "VERSION", dump_short },
00239    { IAX_IE_ADSICPE, "ADSICPE", dump_short },
00240    { IAX_IE_DNID, "DNID", dump_string },
00241    { IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short },
00242    { IAX_IE_CHALLENGE, "CHALLENGE", dump_string_hex },
00243    { IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string },
00244    { IAX_IE_RSA_RESULT, "RSA RESULT", dump_string },
00245    { IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr },
00246    { IAX_IE_REFRESH, "REFRESH", dump_short },
00247    { IAX_IE_DPSTATUS, "DIALPLAN STATUS", dump_short },
00248    { IAX_IE_CALLNO, "CALL NUMBER", dump_short },
00249    { IAX_IE_CAUSE, "CAUSE", dump_string },
00250    { IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte },
00251    { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short },
00252    { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" },
00253    { IAX_IE_TRANSFERID, "TRANSFER ID", dump_int },
00254    { IAX_IE_RDNIS, "REFERRING DNIS", dump_string },
00255    { IAX_IE_PROVISIONING, "PROVISIONING", dump_prov },
00256    { IAX_IE_AESPROVISIONING, "AES PROVISIONG" },
00257    { IAX_IE_DATETIME, "DATE TIME", dump_datetime },
00258    { IAX_IE_DEVICETYPE, "DEVICE TYPE", dump_string },
00259    { IAX_IE_SERVICEIDENT, "SERVICE IDENT", dump_string },
00260    { IAX_IE_FIRMWAREVER, "FIRMWARE VER", dump_short },
00261    { IAX_IE_FWBLOCKDESC, "FW BLOCK DESC", dump_int },
00262    { IAX_IE_FWBLOCKDATA, "FW BLOCK DATA" },
00263    { IAX_IE_PROVVER, "PROVISIONG VER", dump_int },
00264    { IAX_IE_CALLINGPRES, "CALLING PRESNTN", dump_byte },
00265    { IAX_IE_CALLINGTON, "CALLING TYPEOFNUM", dump_byte },
00266    { IAX_IE_CALLINGTNS, "CALLING TRANSITNET", dump_short },
00267    { IAX_IE_SAMPLINGRATE, "SAMPLINGRATE", dump_samprate },
00268    { IAX_IE_CAUSECODE, "CAUSE CODE", dump_byte },
00269    { IAX_IE_ENCRYPTION, "ENCRYPTION", dump_short },
00270    { IAX_IE_ENCKEY, "ENCRYPTION KEY" },
00271    { IAX_IE_CODEC_PREFS, "CODEC_PREFS", dump_prefs },
00272    { IAX_IE_RR_JITTER, "RR_JITTER", dump_int },
00273    { IAX_IE_RR_LOSS, "RR_LOSS", dump_int },
00274    { IAX_IE_RR_PKTS, "RR_PKTS", dump_int },
00275    { IAX_IE_RR_DELAY, "RR_DELAY", dump_short },
00276    { IAX_IE_RR_DROPPED, "RR_DROPPED", dump_int },
00277    { IAX_IE_RR_OOO, "RR_OUTOFORDER", dump_int },
00278    { IAX_IE_VARIABLE, "VARIABLE", dump_string },
00279    { IAX_IE_OSPTOKEN, "OSPTOKEN" },
00280 };
00281 
00282 static struct iax2_ie prov_ies[] = {
00283    { PROV_IE_USEDHCP, "USEDHCP" },
00284    { PROV_IE_IPADDR, "IPADDR", dump_ipaddr },
00285    { PROV_IE_SUBNET, "SUBNET", dump_ipaddr },
00286    { PROV_IE_GATEWAY, "GATEWAY", dump_ipaddr },
00287    { PROV_IE_PORTNO, "BINDPORT", dump_short },
00288    { PROV_IE_USER, "USERNAME", dump_string },
00289    { PROV_IE_PASS, "PASSWORD", dump_string },
00290    { PROV_IE_LANG, "LANGUAGE", dump_string },
00291    { PROV_IE_TOS, "TYPEOFSERVICE", dump_byte },
00292    { PROV_IE_FLAGS, "FLAGS", dump_prov_flags },
00293    { PROV_IE_FORMAT, "FORMAT", dump_int },
00294    { PROV_IE_AESKEY, "AESKEY" },
00295    { PROV_IE_SERVERIP, "SERVERIP", dump_ipaddr },
00296    { PROV_IE_SERVERPORT, "SERVERPORT", dump_short },
00297    { PROV_IE_NEWAESKEY, "NEWAESKEY" },
00298    { PROV_IE_PROVVER, "PROV VERSION", dump_int },
00299    { PROV_IE_ALTSERVER, "ALTSERVERIP", dump_ipaddr },
00300 };
00301 
00302 const char *iax_ie2str(int ie)
00303 {
00304    int x;
00305    for (x = 0; x < ARRAY_LEN(infoelts); x++) {
00306       if (infoelts[x].ie == ie)
00307          return infoelts[x].name;
00308    }
00309    return "Unknown IE";
00310 }
00311 
00312 
00313 static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len)
00314 {
00315    int ielen;
00316    int ie;
00317    int x;
00318    int found;
00319    char interp[80];
00320    char tmp[256];
00321    if (len < 2)
00322       return;
00323    strcpy(output, "\n"); 
00324    maxlen -= strlen(output); output += strlen(output);
00325    while(len > 2) {
00326       ie = iedata[0];
00327       ielen = iedata[1];
00328       if (ielen + 2> len) {
00329          snprintf(tmp, (int)sizeof(tmp), "Total Prov IE length of %d bytes exceeds remaining prov frame length of %d bytes\n", ielen + 2, len);
00330          ast_copy_string(output, tmp, maxlen);
00331          maxlen -= strlen(output);
00332          output += strlen(output);
00333          return;
00334       }
00335       found = 0;
00336       for (x=0;x<(int)sizeof(prov_ies) / (int)sizeof(prov_ies[0]); x++) {
00337          if (prov_ies[x].ie == ie) {
00338             if (prov_ies[x].dump) {
00339                prov_ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
00340                snprintf(tmp, (int)sizeof(tmp), "       %-15.15s : %s\n", prov_ies[x].name, interp);
00341                ast_copy_string(output, tmp, maxlen);
00342                maxlen -= strlen(output); output += strlen(output);
00343             } else {
00344                if (ielen)
00345                   snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
00346                else
00347                   strcpy(interp, "Present");
00348                snprintf(tmp, (int)sizeof(tmp), "       %-15.15s : %s\n", prov_ies[x].name, interp);
00349                ast_copy_string(output, tmp, maxlen);
00350                maxlen -= strlen(output); output += strlen(output);
00351             }
00352             found++;
00353          }
00354       }
00355       if (!found) {
00356          snprintf(tmp, (int)sizeof(tmp), "       Unknown Prov IE %03d  : Present\n", ie);
00357          ast_copy_string(output, tmp, maxlen);
00358          maxlen -= strlen(output); output += strlen(output);
00359       }
00360       iedata += (2 + ielen);
00361       len -= (2 + ielen);
00362    }
00363 }
00364 
00365 static void dump_ies(unsigned char *iedata, int len)
00366 {
00367    int ielen;
00368    int ie;
00369    int x;
00370    int found;
00371    char interp[1024];
00372    char tmp[1024];
00373    if (len < 2)
00374       return;
00375    while(len > 2) {
00376       ie = iedata[0];
00377       ielen = iedata[1];
00378       if (ielen + 2> len) {
00379          snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
00380          outputf(tmp);
00381          return;
00382       }
00383       found = 0;
00384       for (x = 0; x < ARRAY_LEN(infoelts); x++) {
00385          if (infoelts[x].ie == ie) {
00386             if (infoelts[x].dump) {
00387                infoelts[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
00388                snprintf(tmp, (int)sizeof(tmp), "   %-15.15s : %s\n", infoelts[x].name, interp);
00389                outputf(tmp);
00390             } else {
00391                if (ielen)
00392                   snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
00393                else
00394                   strcpy(interp, "Present");
00395                snprintf(tmp, (int)sizeof(tmp), "   %-15.15s : %s\n", infoelts[x].name, interp);
00396                outputf(tmp);
00397             }
00398             found++;
00399          }
00400       }
00401       if (!found) {
00402          snprintf(tmp, (int)sizeof(tmp), "   Unknown IE %03d  : Present\n", ie);
00403          outputf(tmp);
00404       }
00405       iedata += (2 + ielen);
00406       len -= (2 + ielen);
00407    }
00408    outputf("\n");
00409 }
00410 
00411 void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
00412 {
00413    const char *framelist[] = {
00414       "(0?)",
00415       "DTMF_E ",
00416       "VOICE  ",
00417       "VIDEO  ",
00418       "CONTROL",
00419       "NULL   ",
00420       "IAX    ",
00421       "TEXT   ",
00422       "IMAGE  ",
00423       "HTML   ",
00424       "CNG    ",
00425       "MODEM  ",
00426       "DTMF_B ",
00427    };
00428    const char *iaxs[] = {
00429       "(0?)",
00430       "NEW    ",
00431       "PING   ",
00432       "PONG   ",
00433       "ACK    ",
00434       "HANGUP ",
00435       "REJECT ",
00436       "ACCEPT ",
00437       "AUTHREQ",
00438       "AUTHREP",
00439       "INVAL  ",
00440       "LAGRQ  ",
00441       "LAGRP  ",
00442       "REGREQ ",
00443       "REGAUTH",
00444       "REGACK ",
00445       "REGREJ ",
00446       "REGREL ",
00447       "VNAK   ",
00448       "DPREQ  ",
00449       "DPREP  ",
00450       "DIAL   ",
00451       "TXREQ  ",
00452       "TXCNT  ",
00453       "TXACC  ",
00454       "TXREADY",
00455       "TXREL  ",
00456       "TXREJ  ",
00457       "QUELCH ",
00458       "UNQULCH",
00459       "POKE   ",
00460       "PAGE   ",
00461       "MWI    ",
00462       "UNSPRTD",
00463       "TRANSFR",
00464       "PROVISN",
00465       "FWDWNLD",
00466       "FWDATA ",
00467       "TXMEDIA"
00468    };
00469    const char *cmds[] = {
00470       "(0?)",
00471       "HANGUP ",
00472       "RING   ",
00473       "RINGING",
00474       "ANSWER ",
00475       "BUSY   ",
00476       "TKOFFHK",
00477       "OFFHOOK",
00478       "CONGSTN",
00479       "FLASH  ",
00480       "WINK   ",
00481       "OPTION ",
00482       "RDKEY  ",
00483       "RDUNKEY",
00484       "PROGRES",
00485       "PROCDNG",
00486       "HOLD   ",
00487       "UNHOLD ",
00488       "VIDUPDT", };
00489    struct ast_iax2_full_hdr *fh;
00490    char retries[20];
00491    char class2[20];
00492    char subclass2[20];
00493    const char *class;
00494    const char *subclass;
00495    char *dir;
00496    char tmp[512];
00497 
00498    switch(rx) {
00499    case 0:
00500       dir = "Tx";
00501       break;
00502    case 2:
00503       dir = "TE";
00504       break;
00505    case 3:
00506       dir = "RD";
00507       break;
00508    default:
00509       dir = "Rx";
00510       break;
00511    }
00512    if (f) {
00513       fh = f->data;
00514       snprintf(retries, sizeof(retries), "%03d", f->retries);
00515    } else {
00516       fh = fhi;
00517       if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS)
00518          strcpy(retries, "Yes");
00519       else
00520          strcpy(retries, " No");
00521    }
00522    if (!(ntohs(fh->scallno) & IAX_FLAG_FULL)) {
00523       /* Don't mess with mini-frames */
00524       return;
00525    }
00526    if (fh->type >= ARRAY_LEN(framelist)) {
00527       snprintf(class2, sizeof(class2), "(%d?)", fh->type);
00528       class = class2;
00529    } else {
00530       class = framelist[(int)fh->type];
00531    }
00532    if (fh->type == AST_FRAME_DTMF_BEGIN || fh->type == AST_FRAME_DTMF_END) {
00533       sprintf(subclass2, "%c", fh->csub);
00534       subclass = subclass2;
00535    } else if (fh->type == AST_FRAME_IAX) {
00536       if (fh->csub >= (int)sizeof(iaxs)/(int)sizeof(iaxs[0])) {
00537          snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
00538          subclass = subclass2;
00539       } else {
00540          subclass = iaxs[(int)fh->csub];
00541       }
00542    } else if (fh->type == AST_FRAME_CONTROL) {
00543       if (fh->csub >= (int)sizeof(cmds)/(int)sizeof(cmds[0])) {
00544          snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub);
00545          subclass = subclass2;
00546       } else {
00547          subclass = cmds[(int)fh->csub];
00548       }
00549    } else {
00550       snprintf(subclass2, sizeof(subclass2), "%d", fh->csub);
00551       subclass = subclass2;
00552    }
00553    snprintf(tmp, sizeof(tmp), 
00554        "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s\n",
00555        dir,
00556        retries, fh->oseqno, fh->iseqno, class, subclass);
00557    outputf(tmp);
00558    snprintf(tmp, sizeof(tmp), 
00559        "   Timestamp: %05lums  SCall: %5.5d  DCall: %5.5d [%s:%d]\n",
00560        (unsigned long)ntohl(fh->ts),
00561        ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS,
00562        ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
00563    outputf(tmp);
00564    if (fh->type == AST_FRAME_IAX)
00565       dump_ies(fh->iedata, datalen);
00566 }
00567 
00568 int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen)
00569 {
00570    char tmp[256];
00571    if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
00572       snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", iax_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
00573       errorf(tmp);
00574       return -1;
00575    }
00576    ied->buf[ied->pos++] = ie;
00577    ied->buf[ied->pos++] = datalen;
00578    memcpy(ied->buf + ied->pos, data, datalen);
00579    ied->pos += datalen;
00580    return 0;
00581 }
00582 
00583 int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin)
00584 {
00585    return iax_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
00586 }
00587 
00588 int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value) 
00589 {
00590    unsigned int newval;
00591    newval = htonl(value);
00592    return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
00593 }
00594 
00595 int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value) 
00596 {
00597    unsigned short newval;
00598    newval = htons(value);
00599    return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
00600 }
00601 
00602 int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str)
00603 {
00604    return iax_ie_append_raw(ied, ie, str, strlen(str));
00605 }
00606 
00607 int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat)
00608 {
00609    return iax_ie_append_raw(ied, ie, &dat, 1);
00610 }
00611 
00612 int iax_ie_append(struct iax_ie_data *ied, unsigned char ie) 
00613 {
00614    return iax_ie_append_raw(ied, ie, NULL, 0);
00615 }
00616 
00617 void iax_set_output(void (*func)(const char *))
00618 {
00619    outputf = func;
00620 }
00621 
00622 void iax_set_error(void (*func)(const char *))
00623 {
00624    errorf = func;
00625 }
00626 
00627 int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
00628 {
00629    /* Parse data into information elements */
00630    int len;
00631    int ie;
00632    char tmp[256], *tmp2;
00633    struct ast_variable *var, *var2, *prev;
00634    unsigned int count;
00635    memset(ies, 0, (int)sizeof(struct iax_ies));
00636    ies->msgcount = -1;
00637    ies->firmwarever = -1;
00638    ies->calling_ton = -1;
00639    ies->calling_tns = -1;
00640    ies->calling_pres = -1;
00641    ies->samprate = IAX_RATE_8KHZ;
00642    while(datalen >= 2) {
00643       ie = data[0];
00644       len = data[1];
00645       if (len > datalen - 2) {
00646          errorf("Information element length exceeds message size\n");
00647          return -1;
00648       }
00649       switch(ie) {
00650       case IAX_IE_CALLED_NUMBER:
00651          ies->called_number = (char *)data + 2;
00652          break;
00653       case IAX_IE_CALLING_NUMBER:
00654          ies->calling_number = (char *)data + 2;
00655          break;
00656       case IAX_IE_CALLING_ANI:
00657          ies->calling_ani = (char *)data + 2;
00658          break;
00659       case IAX_IE_CALLING_NAME:
00660          ies->calling_name = (char *)data + 2;
00661          break;
00662       case IAX_IE_CALLED_CONTEXT:
00663          ies->called_context = (char *)data + 2;
00664          break;
00665       case IAX_IE_USERNAME:
00666          ies->username = (char *)data + 2;
00667          break;
00668       case IAX_IE_PASSWORD:
00669          ies->password = (char *)data + 2;
00670          break;
00671       case IAX_IE_CODEC_PREFS:
00672          ies->codec_prefs = (char *)data + 2;
00673          break;
00674       case IAX_IE_CAPABILITY:
00675          if (len != (int)sizeof(unsigned int)) {
00676             snprintf(tmp, (int)sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00677             errorf(tmp);
00678          } else
00679             ies->capability = ntohl(get_unaligned_uint32(data + 2));
00680          break;
00681       case IAX_IE_FORMAT:
00682          if (len != (int)sizeof(unsigned int)) {
00683             snprintf(tmp, (int)sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00684             errorf(tmp);
00685          } else
00686             ies->format = ntohl(get_unaligned_uint32(data + 2));
00687          break;
00688       case IAX_IE_LANGUAGE:
00689          ies->language = (char *)data + 2;
00690          break;
00691       case IAX_IE_VERSION:
00692          if (len != (int)sizeof(unsigned short)) {
00693             snprintf(tmp, (int)sizeof(tmp),  "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00694             errorf(tmp);
00695          } else
00696             ies->version = ntohs(get_unaligned_uint16(data + 2));
00697          break;
00698       case IAX_IE_ADSICPE:
00699          if (len != (int)sizeof(unsigned short)) {
00700             snprintf(tmp, (int)sizeof(tmp), "Expecting adsicpe to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00701             errorf(tmp);
00702          } else
00703             ies->adsicpe = ntohs(get_unaligned_uint16(data + 2));
00704          break;
00705       case IAX_IE_SAMPLINGRATE:
00706          if (len != (int)sizeof(unsigned short)) {
00707             snprintf(tmp, (int)sizeof(tmp), "Expecting samplingrate to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00708             errorf(tmp);
00709          } else
00710             ies->samprate = ntohs(get_unaligned_uint16(data + 2));
00711          break;
00712       case IAX_IE_DNID:
00713          ies->dnid = (char *)data + 2;
00714          break;
00715       case IAX_IE_RDNIS:
00716          ies->rdnis = (char *)data + 2;
00717          break;
00718       case IAX_IE_AUTHMETHODS:
00719          if (len != (int)sizeof(unsigned short))  {
00720             snprintf(tmp, (int)sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00721             errorf(tmp);
00722          } else
00723             ies->authmethods = ntohs(get_unaligned_uint16(data + 2));
00724          break;
00725       case IAX_IE_ENCRYPTION:
00726          if (len != (int)sizeof(unsigned short))  {
00727             snprintf(tmp, (int)sizeof(tmp), "Expecting encryption to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00728             errorf(tmp);
00729          } else
00730             ies->encmethods = ntohs(get_unaligned_uint16(data + 2));
00731          break;
00732       case IAX_IE_CHALLENGE:
00733          ies->challenge = (char *)data + 2;
00734          break;
00735       case IAX_IE_MD5_RESULT:
00736          ies->md5_result = (char *)data + 2;
00737          break;
00738       case IAX_IE_RSA_RESULT:
00739          ies->rsa_result = (char *)data + 2;
00740          break;
00741       case IAX_IE_APPARENT_ADDR:
00742          ies->apparent_addr = ((struct sockaddr_in *)(data + 2));
00743          break;
00744       case IAX_IE_REFRESH:
00745          if (len != (int)sizeof(unsigned short)) {
00746             snprintf(tmp, (int)sizeof(tmp),  "Expecting refresh to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00747             errorf(tmp);
00748          } else
00749             ies->refresh = ntohs(get_unaligned_uint16(data + 2));
00750          break;
00751       case IAX_IE_DPSTATUS:
00752          if (len != (int)sizeof(unsigned short)) {
00753             snprintf(tmp, (int)sizeof(tmp),  "Expecting dpstatus to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00754             errorf(tmp);
00755          } else
00756             ies->dpstatus = ntohs(get_unaligned_uint16(data + 2));
00757          break;
00758       case IAX_IE_CALLNO:
00759          if (len != (int)sizeof(unsigned short)) {
00760             snprintf(tmp, (int)sizeof(tmp),  "Expecting callno to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00761             errorf(tmp);
00762          } else
00763             ies->callno = ntohs(get_unaligned_uint16(data + 2));
00764          break;
00765       case IAX_IE_CAUSE:
00766          ies->cause = (char *)data + 2;
00767          break;
00768       case IAX_IE_CAUSECODE:
00769          if (len != 1) {
00770             snprintf(tmp, (int)sizeof(tmp), "Expecting causecode to be single byte but was %d\n", len);
00771             errorf(tmp);
00772          } else {
00773             ies->causecode = data[2];
00774          }
00775          break;
00776       case IAX_IE_IAX_UNKNOWN:
00777          if (len == 1)
00778             ies->iax_unknown = data[2];
00779          else {
00780             snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
00781             errorf(tmp);
00782          }
00783          break;
00784       case IAX_IE_MSGCOUNT:
00785          if (len != (int)sizeof(unsigned short)) {
00786             snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00787             errorf(tmp);
00788          } else
00789             ies->msgcount = ntohs(get_unaligned_uint16(data + 2));   
00790          break;
00791       case IAX_IE_AUTOANSWER:
00792          ies->autoanswer = 1;
00793          break;
00794       case IAX_IE_MUSICONHOLD:
00795          ies->musiconhold = 1;
00796          break;
00797       case IAX_IE_TRANSFERID:
00798          if (len != (int)sizeof(unsigned int)) {
00799             snprintf(tmp, (int)sizeof(tmp), "Expecting transferid to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00800             errorf(tmp);
00801          } else
00802             ies->transferid = ntohl(get_unaligned_uint32(data + 2));
00803          break;
00804       case IAX_IE_DATETIME:
00805          if (len != (int)sizeof(unsigned int)) {
00806             snprintf(tmp, (int)sizeof(tmp), "Expecting date/time to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00807             errorf(tmp);
00808          } else
00809             ies->datetime = ntohl(get_unaligned_uint32(data + 2));
00810          break;
00811       case IAX_IE_FIRMWAREVER:
00812          if (len != (int)sizeof(unsigned short)) {
00813             snprintf(tmp, (int)sizeof(tmp), "Expecting firmwarever to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00814             errorf(tmp);
00815          } else
00816             ies->firmwarever = ntohs(get_unaligned_uint16(data + 2));   
00817          break;
00818       case IAX_IE_DEVICETYPE:
00819          ies->devicetype = (char *)data + 2;
00820          break;
00821       case IAX_IE_SERVICEIDENT:
00822          ies->serviceident = (char *)data + 2;
00823          break;
00824       case IAX_IE_FWBLOCKDESC:
00825          if (len != (int)sizeof(unsigned int)) {
00826             snprintf(tmp, (int)sizeof(tmp), "Expected block desc to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00827             errorf(tmp);
00828          } else
00829             ies->fwdesc = ntohl(get_unaligned_uint32(data + 2));
00830          break;
00831       case IAX_IE_FWBLOCKDATA:
00832          ies->fwdata = data + 2;
00833          ies->fwdatalen = len;
00834          break;
00835       case IAX_IE_ENCKEY:
00836          ies->enckey = data + 2;
00837          ies->enckeylen = len;
00838          break;
00839       case IAX_IE_PROVVER:
00840          if (len != (int)sizeof(unsigned int)) {
00841             snprintf(tmp, (int)sizeof(tmp), "Expected provisioning version to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00842             errorf(tmp);
00843          } else {
00844             ies->provverpres = 1;
00845             ies->provver = ntohl(get_unaligned_uint32(data + 2));
00846          }
00847          break;
00848       case IAX_IE_CALLINGPRES:
00849          if (len == 1)
00850             ies->calling_pres = data[2];
00851          else {
00852             snprintf(tmp, (int)sizeof(tmp), "Expected single byte callingpres, but was %d long\n", len);
00853             errorf(tmp);
00854          }
00855          break;
00856       case IAX_IE_CALLINGTON:
00857          if (len == 1)
00858             ies->calling_ton = data[2];
00859          else {
00860             snprintf(tmp, (int)sizeof(tmp), "Expected single byte callington, but was %d long\n", len);
00861             errorf(tmp);
00862          }
00863          break;
00864       case IAX_IE_CALLINGTNS:
00865          if (len != (int)sizeof(unsigned short)) {
00866             snprintf(tmp, (int)sizeof(tmp), "Expecting callingtns to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00867             errorf(tmp);
00868          } else
00869             ies->calling_tns = ntohs(get_unaligned_uint16(data + 2));   
00870          break;
00871                case IAX_IE_RR_JITTER:
00872                        if (len != (int)sizeof(unsigned int)) {
00873                                snprintf(tmp, (int)sizeof(tmp), "Expected jitter rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00874                                errorf(tmp);
00875                        } else {
00876                                ies->rr_jitter = ntohl(get_unaligned_uint32(data + 2));
00877                        }
00878                        break;
00879                case IAX_IE_RR_LOSS:
00880                        if (len != (int)sizeof(unsigned int)) {
00881                                snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00882                                errorf(tmp);
00883                        } else {
00884                                ies->rr_loss = ntohl(get_unaligned_uint32(data + 2));
00885                        }
00886                        break;
00887                case IAX_IE_RR_PKTS:
00888                        if (len != (int)sizeof(unsigned int)) {
00889                                snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00890                                errorf(tmp);
00891                        } else {
00892                                ies->rr_pkts = ntohl(get_unaligned_uint32(data + 2));
00893                        }
00894                        break;
00895                case IAX_IE_RR_DELAY:
00896                        if (len != (int)sizeof(unsigned short)) {
00897                                snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
00898                         errorf(tmp);
00899                        } else {
00900                                ies->rr_delay = ntohs(get_unaligned_uint16(data + 2));
00901                        }
00902                        break;
00903       case IAX_IE_RR_DROPPED:
00904          if (len != (int)sizeof(unsigned int)) {
00905             snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00906             errorf(tmp);
00907          } else {
00908             ies->rr_dropped = ntohl(get_unaligned_uint32(data + 2));
00909          }
00910          break;
00911       case IAX_IE_RR_OOO:
00912          if (len != (int)sizeof(unsigned int)) {
00913             snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
00914             errorf(tmp);
00915          } else {
00916             ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2));
00917          }
00918          break;
00919       case IAX_IE_VARIABLE:
00920          ast_copy_string(tmp, (char *)data + 2, len + 1);
00921          tmp2 = strchr(tmp, '=');
00922          if (tmp2)
00923             *tmp2++ = '\0';
00924          else
00925             tmp2 = "";
00926          {
00927             struct ast_str *str = ast_str_create(16);
00928             /* Existing variable or new variable? */
00929             for (var2 = ies->vars, prev = NULL; var2; prev = var2, var2 = var2->next) {
00930                if (strcmp(tmp, var2->name) == 0) {
00931                   ast_str_set(&str, 0, "%s%s", var2->value, tmp2);
00932                   var = ast_variable_new(tmp, str->str, var2->file);
00933                   var->next = var2->next;
00934                   if (prev) {
00935                      prev->next = var;
00936                   } else {
00937                      ies->vars = var;
00938                   }
00939                   snprintf(tmp, sizeof(tmp), "Assigned (%p)%s to (%p)%s\n", var->name, var->name, var->value, var->value);
00940                   errorf(tmp);
00941                   ast_free(var2);
00942                   break;
00943                }
00944             }
00945             ast_free(str);
00946          }
00947 
00948          if (!var2) {
00949             var = ast_variable_new(tmp, tmp2, "");
00950             snprintf(tmp, sizeof(tmp), "Assigned (%p)%s to (%p)%s\n", var->name, var->name, var->value, var->value);
00951             errorf(tmp);
00952             var->next = ies->vars;
00953             ies->vars = var;
00954          }
00955          break;
00956       case IAX_IE_OSPTOKEN:
00957          if ((count = data[2]) < IAX_MAX_OSPBLOCK_NUM) {
00958             ies->osptokenblock[count] = (char *)data + 2 + 1;
00959             ies->ospblocklength[count] = len - 1;
00960          } else {
00961             snprintf(tmp, (int)sizeof(tmp), "Expected OSP token block index to be 0~%d but was %d\n", IAX_MAX_OSPBLOCK_NUM - 1, count);
00962             errorf(tmp);
00963          }
00964          break;
00965       default:
00966          snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
00967          outputf(tmp);
00968       }
00969       /* Overwrite information element with 0, to null terminate previous portion */
00970       data[0] = 0;
00971       datalen -= (len + 2);
00972       data += (len + 2);
00973    }
00974    /* Null-terminate last field */
00975    *data = '\0';
00976    if (datalen) {
00977       errorf("Invalid information element contents, strange boundary\n");
00978       return -1;
00979    }
00980    return 0;
00981 }
00982 
00983 void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f)
00984 {
00985    fr->af.frametype = f->frametype;
00986    fr->af.subclass = f->subclass;
00987    fr->af.mallocd = 0;           /* Our frame is static relative to the container */
00988    fr->af.datalen = f->datalen;
00989    fr->af.samples = f->samples;
00990    fr->af.offset = AST_FRIENDLY_OFFSET;
00991    fr->af.src = f->src;
00992    fr->af.delivery.tv_sec = 0;
00993    fr->af.delivery.tv_usec = 0;
00994    fr->af.data.ptr = fr->afdata;
00995    fr->af.len = f->len;
00996    if (fr->af.datalen) {
00997       size_t copy_len = fr->af.datalen;
00998       if (copy_len > fr->afdatalen) {
00999          ast_log(LOG_ERROR, "Losing frame data because destination buffer size '%d' bytes not big enough for '%d' bytes in the frame\n",
01000             (int) fr->afdatalen, (int) fr->af.datalen);
01001          copy_len = fr->afdatalen;
01002       }
01003 #if __BYTE_ORDER == __LITTLE_ENDIAN
01004       /* We need to byte-swap slinear samples from network byte order */
01005       if ((fr->af.frametype == AST_FRAME_VOICE) && (fr->af.subclass == AST_FORMAT_SLINEAR)) {
01006          /* 2 bytes / sample for SLINEAR */
01007          ast_swapcopy_samples(fr->af.data.ptr, f->data.ptr, copy_len / 2);
01008       } else
01009 #endif
01010          memcpy(fr->af.data.ptr, f->data.ptr, copy_len);
01011    }
01012 }
01013 
01014 struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable)
01015 {
01016    struct iax_frame *fr = NULL;
01017 
01018 #if !defined(LOW_MEMORY)
01019    struct iax_frames *iax_frames = NULL;
01020    struct iax_frame *smallest = NULL;
01021 
01022    /* Attempt to get a frame from this thread's cache */
01023    if ((iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
01024       smallest = AST_LIST_FIRST(&iax_frames->list);
01025       AST_LIST_TRAVERSE_SAFE_BEGIN(&iax_frames->list, fr, list) {
01026          if (fr->afdatalen >= datalen) {
01027             size_t afdatalen = fr->afdatalen;
01028             AST_LIST_REMOVE_CURRENT(list);
01029             iax_frames->size--;
01030             memset(fr, 0, sizeof(*fr));
01031             fr->afdatalen = afdatalen;
01032             break;
01033          } else if (smallest->afdatalen > fr->afdatalen) {
01034             smallest = fr;
01035          }
01036       }
01037       AST_LIST_TRAVERSE_SAFE_END;
01038    }
01039    if (!fr) {
01040       if (iax_frames->size >= FRAME_CACHE_MAX_SIZE && smallest) {
01041          /* Make useless cache into something more useful */
01042          AST_LIST_REMOVE(&iax_frames->list, smallest, list);
01043          if (!(fr = ast_realloc(smallest, sizeof(*fr) + datalen))) {
01044             AST_LIST_INSERT_TAIL(&iax_frames->list, smallest, list);
01045             return NULL;
01046          }
01047       } else if (!(fr = ast_calloc_cache(1, sizeof(*fr) + datalen)))
01048          return NULL;
01049       fr->afdatalen = datalen;
01050    }
01051 #else
01052    if (!(fr = ast_calloc(1, sizeof(*fr) + datalen)))
01053       return NULL;
01054    fr->afdatalen = datalen;
01055 #endif
01056 
01057 
01058    fr->direction = direction;
01059    fr->retrans = -1;
01060    fr->cacheable = cacheable;
01061    
01062    if (fr->direction == DIRECTION_INGRESS)
01063       ast_atomic_fetchadd_int(&iframes, 1);
01064    else
01065       ast_atomic_fetchadd_int(&oframes, 1);
01066    
01067    ast_atomic_fetchadd_int(&frames, 1);
01068 
01069    return fr;
01070 }
01071 
01072 void iax_frame_free(struct iax_frame *fr)
01073 {
01074 #if !defined(LOW_MEMORY)
01075    struct iax_frames *iax_frames = NULL;
01076 #endif
01077 
01078    /* Note: does not remove from scheduler! */
01079    if (fr->direction == DIRECTION_INGRESS)
01080       ast_atomic_fetchadd_int(&iframes, -1);
01081    else if (fr->direction == DIRECTION_OUTGRESS)
01082       ast_atomic_fetchadd_int(&oframes, -1);
01083    else {
01084       errorf("Attempt to double free frame detected\n");
01085       return;
01086    }
01087    ast_atomic_fetchadd_int(&frames, -1);
01088 
01089 #if !defined(LOW_MEMORY)
01090    if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
01091       ast_free(fr);
01092       return;
01093    }
01094 
01095    if (iax_frames->size < FRAME_CACHE_MAX_SIZE) {
01096       fr->direction = 0;
01097       /* Pseudo-sort: keep smaller frames at the top of the list. This should
01098        * increase the chance that we pick the smallest applicable frame for use. */
01099       if (AST_LIST_FIRST(&iax_frames->list) && AST_LIST_FIRST(&iax_frames->list)->afdatalen < fr->afdatalen) {
01100          AST_LIST_INSERT_TAIL(&iax_frames->list, fr, list);
01101       } else {
01102          AST_LIST_INSERT_HEAD(&iax_frames->list, fr, list);
01103       }
01104       iax_frames->size++;
01105       return;
01106    }
01107 #endif
01108    ast_free(fr);
01109 }
01110 
01111 #if !defined(LOW_MEMORY)
01112 static void frame_cache_cleanup(void *data)
01113 {
01114    struct iax_frames *framelist = data;
01115    struct iax_frame *current;
01116 
01117    while ((current = AST_LIST_REMOVE_HEAD(&framelist->list, list)))
01118       ast_free(current);
01119 
01120    ast_free(framelist);
01121 }
01122 #endif
01123 
01124 int iax_get_frames(void) { return frames; }
01125 int iax_get_iframes(void) { return iframes; }
01126 int iax_get_oframes(void) { return oframes; }

Generated on Fri Jun 19 12:09:46 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7