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