libpri-1.4.3/0000755000000000000000000000000010730327620011476 5ustar rootrootlibpri-1.4.3/pri_timers.h0000644000000000000000000000520110374426762014035 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Linux Support Services, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _PRI_TIMERS_H #define _PRI_TIMERS_H /* -1 means we dont currently support the timer/counter */ #define PRI_TIMERS_DEFAULT { 3, /* N200 */ \ -1, /* N201 */ \ -1, /* N202 */ \ 7, /* K */ \ 1000, /* T200 */ \ -1, /* T201 */ \ -1, /* T202 */ \ 10000, /* T203 */ \ -1, /* T300 */ \ -1, /* T301 */ \ -1, /* T302 */ \ -1, /* T303 */ \ -1, /* T304 */ \ 30000, /* T305 */ \ -1, /* T306 */ \ -1, /* T307 */ \ 4000, /* T308 */ \ -1, /* T309 */ \ -1, /* T310 */ \ 4000, /* T313 */ \ -1, /* T314 */ \ -1, /* T316 */ \ -1, /* T317 */ \ -1, /* T318 */ \ -1, /* T319 */ \ -1, /* T320 */ \ -1, /* T321 */ \ -1 /* T322 */ \ } /* XXX Only our default timers are setup now XXX */ #define PRI_TIMERS_UNKNOWN PRI_TIMERS_DEFAULT #define PRI_TIMERS_NI2 PRI_TIMERS_DEFAULT #define PRI_TIMERS_DMS100 PRI_TIMERS_DEFAULT #define PRI_TIMERS_LUCENT5E PRI_TIMERS_DEFAULT #define PRI_TIMERS_ATT4ESS PRI_TIMERS_DEFAULT #define PRI_TIMERS_EUROISDN_E1 PRI_TIMERS_DEFAULT #define PRI_TIMERS_EUROISDN_T1 PRI_TIMERS_DEFAULT #define PRI_TIMERS_NI1 PRI_TIMERS_DEFAULT #define PRI_TIMERS_GR303_EOC PRI_TIMERS_DEFAULT #define PRI_TIMERS_GR303_TMC PRI_TIMERS_DEFAULT #define PRI_TIMERS_QSIG PRI_TIMERS_DEFAULT #define __PRI_TIMERS_GR303_EOC_INT PRI_TIMERS_DEFAULT #define __PRI_TIMERS_GR303_TMC_INT PRI_TIMERS_DEFAULT #define PRI_TIMERS_ALL { PRI_TIMERS_UNKNOWN, \ PRI_TIMERS_NI2, \ PRI_TIMERS_DMS100, \ PRI_TIMERS_LUCENT5E, \ PRI_TIMERS_ATT4ESS, \ PRI_TIMERS_EUROISDN_E1, \ PRI_TIMERS_EUROISDN_T1, \ PRI_TIMERS_NI1, \ PRI_TIMERS_QSIG, \ PRI_TIMERS_GR303_EOC, \ PRI_TIMERS_GR303_TMC, \ __PRI_TIMERS_GR303_EOC_INT, \ __PRI_TIMERS_GR303_TMC_INT, \ } #endif libpri-1.4.3/.version0000644000000000000000000000000610730327740013163 0ustar rootroot1.4.3 libpri-1.4.3/README0000644000000000000000000000323610374666137012377 0ustar rootrootlibpri: An implementation of Primary Rate ISDN Written by Mark Spencer What is libpri? =============== libpri is a C implementation of the Primary Rate ISDN specification. It was based on the Bellcore specification SR-NWT-002343 for National ISDN. As of May 12, 2001, it has been tested work with NI-2, Nortel DMS-100, and Lucent 5E Custom protocols on switches from Nortel and Lucent. What is the license for libpri? =============================== libpri is distributed under the terms of the GNU General Public License, which permit its use and linking with other GPL'd software only. The GNU GPL is included in the file LICENSE in this directory. As a special exception, libpri may also be linked to the OpenH323 library, so long as the entirity of the derivative work (as defined within the GPL) is licensed either under the MPL of the OpenH323 license or the GPL of libpri. If you wish to use libpri in an application for which the GPL is not appropriate (e.g. a proprietary embedded system), licenses for libpri under more flexible terms can be readily obtained through Digium, Inc. at reasonable cost. How do I report bugs or contribute? =================================== For now, contact the author directly. In the future if there is sufficient interest, we will setup a mailing list. Does anything use this library so far? ====================================== Yes, the Asterisk Open Source PBX does. http://www.asterisk.org Also, the Zapata library has hooks for it. http://www.zapatatelephony.org Special thanks ============== Special thanks to Jim Dixon for his help in testing and fixing the implementation. libpri-1.4.3/copy_string.c0000644000000000000000000000224110374426762014214 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include "compiler.h" #include "libpri.h" #include "pri_internal.h" void libpri_copy_string(char *dst, const char *src, size_t size) { while (*src && size) { *dst++ = *src++; size--; } if (__builtin_expect(!size, 0)) dst--; *dst = '\0'; } libpri-1.4.3/LICENSE0000644000000000000000000004313010374426762012517 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libpri-1.4.3/prisched.c0000644000000000000000000000615010374426762013460 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include "libpri.h" #include "pri_internal.h" static int maxsched = 0; /* Scheduler routines */ int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data) { int x; struct timeval tv; for (x=1;xpri_sched[x].callback) break; if (x == MAX_SCHED) { pri_error(pri, "No more room in scheduler\n"); return -1; } if (x > maxsched) maxsched = x; gettimeofday(&tv, NULL); tv.tv_sec += ms / 1000; tv.tv_usec += (ms % 1000) * 1000; if (tv.tv_usec > 1000000) { tv.tv_usec -= 1000000; tv.tv_sec += 1; } pri->pri_sched[x].when = tv; pri->pri_sched[x].callback = function; pri->pri_sched[x].data = data; return x; } struct timeval *pri_schedule_next(struct pri *pri) { struct timeval *closest = NULL; int x; /* Check subchannels */ if (pri->subchannel) closest = pri_schedule_next(pri->subchannel); for (x=1;xpri_sched[x].callback && (!closest || (closest->tv_sec > pri->pri_sched[x].when.tv_sec) || ((closest->tv_sec == pri->pri_sched[x].when.tv_sec) && (closest->tv_usec > pri->pri_sched[x].when.tv_usec)))) closest = &pri->pri_sched[x].when; } return closest; } static pri_event *__pri_schedule_run(struct pri *pri, struct timeval *tv) { int x; void (*callback)(void *); void *data; pri_event *e; if (pri->subchannel) { if ((e = __pri_schedule_run(pri->subchannel, tv))) { return e; } } for (x=1;xpri_sched[x].callback && ((pri->pri_sched[x].when.tv_sec < tv->tv_sec) || ((pri->pri_sched[x].when.tv_sec == tv->tv_sec) && (pri->pri_sched[x].when.tv_usec <= tv->tv_usec)))) { pri->schedev = 0; callback = pri->pri_sched[x].callback; data = pri->pri_sched[x].data; pri->pri_sched[x].callback = NULL; pri->pri_sched[x].data = NULL; callback(data); if (pri->schedev) return &pri->ev; } } return NULL; } pri_event *pri_schedule_run(struct pri *pri) { struct timeval tv; gettimeofday(&tv, NULL); return __pri_schedule_run(pri, &tv); } void pri_schedule_del(struct pri *pri,int id) { if ((id >= MAX_SCHED) || (id < 0)) pri_error(pri, "Asked to delete sched id %d???\n", id); pri->pri_sched[id].callback = NULL; } libpri-1.4.3/ChangeLog0000644000000000000000000000741310730327740013260 0ustar rootroot2007-12-13 Russell Bryant * libpri 1.4.3 released. 2007-10-22 15:10 +0000 [r479] Kevin P. Fleming * pri_internal.h, /: Merged revisions 478 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r478 | kpfleming | 2007-10-22 10:09:27 -0500 (Mon, 22 Oct 2007) | 3 lines we need to include stddef.h for 'size_t' ........ 2007-10-16 Matthew Fredrickson * libpri 1.4.2 released. 2007-10-05 16:45 +0000 [r470] Jason Parker * libpri.h: Fix an incorrect pri_event structure definition. Issue 10832, patch by flefoll 2007-09-25 21:37 +0000 [r468] Matthew Fredrickson * q931.c: Fix user-user IE order in setup message (#10705) 2007-09-14 21:32 +0000 [r465] Matthew Fredrickson * q931.c, libpri.h: Fix for #10189. Make sure we properly report the user layer 1 for H.223 and H.245 2007-09-06 15:11 +0000 [r462] Matthew Fredrickson * pri.c, pri_facility.c, /: Merged revisions 460 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r460 | mattf | 2007-09-06 10:06:42 -0500 (Thu, 06 Sep 2007) | 1 line TBCT now works. It should work for NI2, 4E, and 5E. This code was tested on NI2. ........ 2007-08-27 19:21 +0000 [r446] Jason Parker * Makefile, /: Merged revisions 445 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r445 | qwell | 2007-08-27 14:20:23 -0500 (Mon, 27 Aug 2007) | 2 lines Make sure we build both the static and shared modules with -fPIC. ........ 2007-08-06 19:58 +0000 [r441] Jason Parker * Makefile: Allow setting CC to something with a space, such as `make CC="gcc -m32"` Issue 10253. 2007-07-09 Russell Bryant * libpri 1.4.1 released. 2007-06-19 18:23 +0000 [r427] Matthew Fredrickson * q931.c, /: Merged revisions 426 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r426 | mattf | 2007-06-19 13:22:33 -0500 (Tue, 19 Jun 2007) | 1 line Try to send pending facility messages if we receive alerting (for when we don't get proceeding) (#9651) ........ 2007-06-06 21:58 +0000 [r416-424] Matthew Fredrickson * pri_facility.c, /: Merged revisions 423 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r423 | mattf | 2007-06-06 16:57:15 -0500 (Wed, 06 Jun 2007) | 1 line Oops, that should not be on one line ........ * pri_facility.c, /: Merged revisions 415 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r415 | mattf | 2007-06-06 08:20:05 -0500 (Wed, 06 Jun 2007) | 2 lines Make sure we only send the NFE when we are talking QSIG ........ 2007-01-22 22:29 +0000 [r390] Matthew Fredrickson * q931.c, /: Merged revisions 389 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r389 | mattf | 2007-01-22 16:20:59 -0600 (Mon, 22 Jan 2007) | 2 lines Make sure we send DISCONNECT if we reached the active state and a call is disconnected, regardless of cause code. ........ 2006-12-30 19:17 +0000 [r386] Joshua Colp * Makefile, /: Merged revisions 385 via svnmerge from https://origsvn.digium.com/svn/libpri/branches/1.2 ........ r385 | file | 2006-12-30 14:16:17 -0500 (Sat, 30 Dec 2006) | 2 lines Specify full path to restorecon instead of assuming it will be available from what is in $PATH. (issue #8670 reported by djflux) ........ 2006-12-23 Kevin P. Fleming * libpri 1.4.0 released. 2006-09-20 Kevin P. Fleming * libpri 1.4.0-beta1 released. libpri-1.4.3/pri_q921.h0000644000000000000000000001227310374426762013235 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Linux Support Services, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _PRI_Q921_H #define _PRI_Q921_H #include #if defined(__linux__) #include #elif defined(__FreeBSD__) #include #define __BYTE_ORDER _BYTE_ORDER #define __BIG_ENDIAN _BIG_ENDIAN #define __LITTLE_ENDIAN _LITTLE_ENDIAN #endif /* Timer values */ #define T_WAIT_MIN 2000 #define T_WAIT_MAX 10000 #define Q921_FRAMETYPE_MASK 0x3 #define Q921_FRAMETYPE_U 0x3 #define Q921_FRAMETYPE_I 0x0 #define Q921_FRAMETYPE_S 0x1 #define Q921_TEI_GROUP 127 #define Q921_TEI_GR303_EOC_PATH 0 #define Q921_TEI_GR303_EOC_OPS 4 #define Q921_TEI_GR303_TMC_SWITCHING 0 #define Q921_TEI_GR303_TMC_CALLPROC 0 #define Q921_SAPI_CALL_CTRL 0 #define Q921_SAPI_GR303_EOC 1 #define Q921_SAPI_GR303_TMC_SWITCHING 1 #define Q921_SAPI_GR303_TMC_CALLPROC 0 #define Q921_SAPI_PACKET_MODE 1 #define Q921_SAPI_X25_LAYER3 16 #define Q921_SAPI_LAYER2_MANAGEMENT 63 typedef struct q921_header { #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t sapi:6; /* Service Access Point Indentifier (always 0 for PRI) (0) */ u_int8_t c_r:1; /* Command/Response (0 if CPE, 1 if network) */ u_int8_t ea1:1; /* Extended Address (0) */ u_int8_t tei:7; /* Terminal Endpoint Identifier (0) */ u_int8_t ea2:1; /* Extended Address Bit (1) */ #else u_int8_t ea1:1; /* Extended Address (0) */ u_int8_t c_r:1; /* Command/Response (0 if CPE, 1 if network) */ u_int8_t sapi:6; /* Service Access Point Indentifier (always 0 for PRI) (0) */ u_int8_t ea2:1; /* Extended Address Bit (1) */ u_int8_t tei:7; /* Terminal Endpoint Identifier (0) */ #endif u_int8_t data[0]; /* Further data */ } __attribute__ ((packed)) q921_header; /* A Supervisory Format frame */ typedef struct q921_s { struct q921_header h; /* Header */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t x0:4; /* Unused */ u_int8_t ss:2; /* Supervisory frame bits */ u_int8_t ft:2; /* Frame type bits (01) */ u_int8_t n_r:7; /* Number Received */ u_int8_t p_f:1; /* Poll/Final bit */ #else u_int8_t ft:2; /* Frame type bits (01) */ u_int8_t ss:2; /* Supervisory frame bits */ u_int8_t x0:4; /* Unused */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t n_r:7; /* Number Received */ #endif u_int8_t data[0]; /* Any further data */ u_int8_t fcs[2]; /* At least an FCS */ } __attribute__ ((packed)) q921_s; /* An Unnumbered Format frame */ typedef struct q921_u { struct q921_header h; /* Header */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t m3:3; /* Top 3 modifier bits */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t m2:2; /* Two more modifier bits */ u_int8_t ft:2; /* Frame type bits (11) */ #else u_int8_t ft:2; /* Frame type bits (11) */ u_int8_t m2:2; /* Two more modifier bits */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t m3:3; /* Top 3 modifier bits */ #endif u_int8_t data[0]; /* Any further data */ u_int8_t fcs[2]; /* At least an FCS */ } __attribute__ ((packed)) q921_u; /* An Information frame */ typedef struct q921_i { struct q921_header h; /* Header */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t n_s:7; /* Number sent */ u_int8_t ft:1; /* Frame type (0) */ u_int8_t n_r:7; /* Number received */ u_int8_t p_f:1; /* Poll/Final bit */ #else u_int8_t ft:1; /* Frame type (0) */ u_int8_t n_s:7; /* Number sent */ u_int8_t p_f:1; /* Poll/Final bit */ u_int8_t n_r:7; /* Number received */ #endif u_int8_t data[0]; /* Any further data */ u_int8_t fcs[2]; /* At least an FCS */ } q921_i; typedef union { u_int8_t raw[0]; q921_u u; q921_s s; q921_i i; struct q921_header h; } q921_h; typedef struct q921_frame { struct q921_frame *next; /* Next in list */ int len; /* Length of header + body */ int transmitted; /* Have we been transmitted */ q921_i h; } q921_frame; #define Q921_INC(j) (j) = (((j) + 1) % 128) typedef enum q921_state { Q921_LINK_CONNECTION_RELEASED, /* Also known as TEI_ASSIGNED */ Q921_LINK_CONNECTION_ESTABLISHED, Q921_AWAITING_ESTABLISH, Q921_AWAITING_RELEASE } q921_state; /* Dumps a *known good* Q.921 packet */ extern void q921_dump(struct pri *pri, q921_h *h, int len, int showraw, int txrx); /* Bring up the D-channel */ extern void q921_start(struct pri *pri, int now); extern void q921_reset(struct pri *pri); extern pri_event *q921_receive(struct pri *pri, q921_h *h, int len); extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr); #endif libpri-1.4.3/pri_facility.c0000644000000000000000000015412210670014241014320 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Matthew Fredrickson * * Copyright (C) 2004-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_q921.h" #include "pri_q931.h" #include "pri_facility.h" #include #include #include #include static char *asn1id2text(int id) { static char data[32]; static char *strings[] = { "none", "Boolean", "Integer", "Bit String", "Octet String", "NULL", "Object Identifier", "Object Descriptor", "External Reference", "Real Number", "Enumerated", "Embedded PDV", "UTF-8 String", "Relative Object ID", "Reserved (0e)", "Reserved (0f)", "Sequence", "Set", "Numeric String", "Printable String", "Tele-Text String", "IA-5 String", "UTC Time", "Generalized Time", }; if (id > 0 && id <= 0x18) { return strings[id]; } else { sprintf(data, "Unknown (%02x)", id); return data; } } static int asn1_dumprecursive(struct pri *pri, void *comp_ptr, int len, int level) { unsigned char *vdata = (unsigned char *)comp_ptr; struct rose_component *comp; int i = 0; int j, k, l; int clen = 0; while (len > 0) { GET_COMPONENT(comp, i, vdata, len); pri_message(pri, "%*s%02X %04X", 2 * level, "", comp->type, comp->len); if ((comp->type == 0) && (comp->len == 0)) return clen + 2; if ((comp->type & ASN1_PC_MASK) == ASN1_PRIMITIVE) { for (j = 0; j < comp->len; ++j) pri_message(pri, " %02X", comp->data[j]); } if ((comp->type & ASN1_CLAN_MASK) == ASN1_UNIVERSAL) { switch (comp->type & ASN1_TYPE_MASK) { case 0: pri_message(pri, " (none)"); break; case ASN1_BOOLEAN: pri_message(pri, " (BOOLEAN: %d)", comp->data[0]); break; case ASN1_INTEGER: for (k = l = 0; k < comp->len; ++k) l = (l << 8) | comp->data[k]; pri_message(pri, " (INTEGER: %d)", l); break; case ASN1_BITSTRING: pri_message(pri, " (BITSTRING:"); for (k = 0; k < comp->len; ++k) pri_message(pri, " %02x", comp->data[k]); pri_message(pri, ")"); break; case ASN1_OCTETSTRING: pri_message(pri, " (OCTETSTRING:"); for (k = 0; k < comp->len; ++k) pri_message(pri, " %02x", comp->data[k]); pri_message(pri, ")"); break; case ASN1_NULL: pri_message(pri, " (NULL)"); break; case ASN1_OBJECTIDENTIFIER: pri_message(pri, " (OBJECTIDENTIFIER:"); for (k = 0; k < comp->len; ++k) pri_message(pri, " %02x", comp->data[k]); pri_message(pri, ")"); break; case ASN1_ENUMERATED: for (k = l = 0; k < comp->len; ++k) l = (l << 8) | comp->data[k]; pri_message(pri, " (ENUMERATED: %d)", l); break; case ASN1_SEQUENCE: pri_message(pri, " (SEQUENCE)"); break; default: pri_message(pri, " (component %02x - %s)", comp->type, asn1id2text(comp->type & ASN1_TYPE_MASK)); break; } } else if ((comp->type & ASN1_CLAN_MASK) == ASN1_CONTEXT_SPECIFIC) { pri_message(pri, " (CONTEXT SPECIFIC [%d])", comp->type & ASN1_TYPE_MASK); } else { pri_message(pri, " (component %02x)", comp->type); } pri_message(pri, "\n"); if ((comp->type & ASN1_PC_MASK) == ASN1_CONSTRUCTOR) j = asn1_dumprecursive(pri, comp->data, (comp->len ? comp->len : INT_MAX), level+1); else j = comp->len; j += 2; len -= j; vdata += j; clen += j; } return clen; } int asn1_dump(struct pri *pri, void *comp, int len) { return asn1_dumprecursive(pri, comp, len, 0); } static unsigned char get_invokeid(struct pri *pri) { return ++pri->last_invoke; } struct addressingdataelements_presentednumberunscreened { char partyaddress[21]; char partysubaddress[21]; int npi; int ton; int pres; }; #define PRI_CHECKOVERFLOW(size) \ if (msgptr - message + (size) >= sizeof(message)) { \ *msgptr = '\0'; \ pri_message(pri, "%s", message); \ msgptr = message; \ } static void dump_apdu(struct pri *pri, unsigned char *c, int len) { #define MAX_APDU_LENGTH 255 static char hexs[16] = "0123456789ABCDEF"; int i; char message[(2 + MAX_APDU_LENGTH * 3 + 6 + MAX_APDU_LENGTH + 3)] = ""; /* please adjust here, if you make changes below! */ char *msgptr; msgptr = message; *msgptr++ = ' '; *msgptr++ = '['; for (i=0; i> 4) & 0x0f]; *msgptr++ = hexs[(c[i]) & 0x0f]; } PRI_CHECKOVERFLOW(6); strcpy(msgptr, " ] - ["); msgptr += strlen(msgptr); for (i=0; i '~')) ? '.' : c[i]; } PRI_CHECKOVERFLOW(2); *msgptr++ = ']'; *msgptr++ = '\n'; *msgptr = '\0'; pri_message(pri, "%s", message); } #undef PRI_CHECKOVERFLOW int redirectingreason_from_q931(struct pri *pri, int redirectingreason) { switch(pri->switchtype) { case PRI_SWITCH_QSIG: switch(redirectingreason) { case PRI_REDIR_UNKNOWN: return QSIG_DIVERT_REASON_UNKNOWN; case PRI_REDIR_FORWARD_ON_BUSY: return QSIG_DIVERT_REASON_CFB; case PRI_REDIR_FORWARD_ON_NO_REPLY: return QSIG_DIVERT_REASON_CFNR; case PRI_REDIR_UNCONDITIONAL: return QSIG_DIVERT_REASON_CFU; case PRI_REDIR_DEFLECTION: case PRI_REDIR_DTE_OUT_OF_ORDER: case PRI_REDIR_FORWARDED_BY_DTE: pri_message(pri, "!! Don't know how to convert Q.931 redirection reason %d to Q.SIG\n", redirectingreason); /* Fall through */ default: return QSIG_DIVERT_REASON_UNKNOWN; } default: switch(redirectingreason) { case PRI_REDIR_UNKNOWN: return Q952_DIVERT_REASON_UNKNOWN; case PRI_REDIR_FORWARD_ON_BUSY: return Q952_DIVERT_REASON_CFB; case PRI_REDIR_FORWARD_ON_NO_REPLY: return Q952_DIVERT_REASON_CFNR; case PRI_REDIR_DEFLECTION: return Q952_DIVERT_REASON_CD; case PRI_REDIR_UNCONDITIONAL: return Q952_DIVERT_REASON_CFU; case PRI_REDIR_DTE_OUT_OF_ORDER: case PRI_REDIR_FORWARDED_BY_DTE: pri_message(pri, "!! Don't know how to convert Q.931 redirection reason %d to Q.952\n", redirectingreason); /* Fall through */ default: return Q952_DIVERT_REASON_UNKNOWN; } } } static int redirectingreason_for_q931(struct pri *pri, int redirectingreason) { switch(pri->switchtype) { case PRI_SWITCH_QSIG: switch(redirectingreason) { case QSIG_DIVERT_REASON_UNKNOWN: return PRI_REDIR_UNKNOWN; case QSIG_DIVERT_REASON_CFU: return PRI_REDIR_UNCONDITIONAL; case QSIG_DIVERT_REASON_CFB: return PRI_REDIR_FORWARD_ON_BUSY; case QSIG_DIVERT_REASON_CFNR: return PRI_REDIR_FORWARD_ON_NO_REPLY; default: pri_message(pri, "!! Unknown Q.SIG diversion reason %d\n", redirectingreason); return PRI_REDIR_UNKNOWN; } default: switch(redirectingreason) { case Q952_DIVERT_REASON_UNKNOWN: return PRI_REDIR_UNKNOWN; case Q952_DIVERT_REASON_CFU: return PRI_REDIR_UNCONDITIONAL; case Q952_DIVERT_REASON_CFB: return PRI_REDIR_FORWARD_ON_BUSY; case Q952_DIVERT_REASON_CFNR: return PRI_REDIR_FORWARD_ON_NO_REPLY; case Q952_DIVERT_REASON_CD: return PRI_REDIR_DEFLECTION; case Q952_DIVERT_REASON_IMMEDIATE: pri_message(pri, "!! Dont' know how to convert Q.952 diversion reason IMMEDIATE to PRI analog\n"); return PRI_REDIR_UNKNOWN; /* ??? */ default: pri_message(pri, "!! Unknown Q.952 diversion reason %d\n", redirectingreason); return PRI_REDIR_UNKNOWN; } } } int typeofnumber_from_q931(struct pri *pri, int ton) { switch(ton) { case PRI_TON_INTERNATIONAL: return Q932_TON_INTERNATIONAL; case PRI_TON_NATIONAL: return Q932_TON_NATIONAL; case PRI_TON_NET_SPECIFIC: return Q932_TON_NET_SPECIFIC; case PRI_TON_SUBSCRIBER: return Q932_TON_SUBSCRIBER; case PRI_TON_ABBREVIATED: return Q932_TON_ABBREVIATED; case PRI_TON_RESERVED: default: pri_message(pri, "!! Unsupported Q.931 TypeOfNumber value (%d)\n", ton); /* fall through */ case PRI_TON_UNKNOWN: return Q932_TON_UNKNOWN; } } static int typeofnumber_for_q931(struct pri *pri, int ton) { switch (ton) { case Q932_TON_UNKNOWN: return PRI_TON_UNKNOWN; case Q932_TON_INTERNATIONAL: return PRI_TON_INTERNATIONAL; case Q932_TON_NATIONAL: return PRI_TON_NATIONAL; case Q932_TON_NET_SPECIFIC: return PRI_TON_NET_SPECIFIC; case Q932_TON_SUBSCRIBER: return PRI_TON_SUBSCRIBER; case Q932_TON_ABBREVIATED: return PRI_TON_ABBREVIATED; default: pri_message(pri, "!! Invalid Q.932 TypeOfNumber %d\n", ton); return PRI_TON_UNKNOWN; } } int asn1_name_decode(void * data, int len, char *namebuf, int buflen) { struct rose_component *comp = (struct rose_component*)data; int datalen = 0, res = 0; if (comp->len == ASN1_LEN_INDEF) { datalen = strlen((char *)comp->data); res = datalen + 2; } else datalen = res = comp->len; if (datalen > buflen) { /* Truncate */ datalen = buflen; } memcpy(namebuf, comp->data, datalen); return res + 2; } int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len) { struct rose_component *comp = NULL; if (len < 2 + src_len) return -1; if (max_len && (src_len > max_len)) src_len = max_len; comp = (struct rose_component *)data; comp->type = asn1_type; comp->len = src_len; memcpy(comp->data, src, src_len); return 2 + src_len; } int asn1_copy_string(char * buf, int buflen, struct rose_component *comp) { int res; int datalen; if ((comp->len > buflen) && (comp->len != ASN1_LEN_INDEF)) return -1; if (comp->len == ASN1_LEN_INDEF) { datalen = strlen((char*)comp->data); res = datalen + 2; } else res = datalen = comp->len; memcpy(buf, comp->data, datalen); buf[datalen] = 0; return res; } static int rose_number_digits_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) { int i = 0; struct rose_component *comp = NULL; unsigned char *vdata = data; int datalen = 0; int res = 0; do { GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_NUMERICSTRING, "Don't know what to do with PublicPartyNumber ROSE component type 0x%x\n"); if(comp->len > 20 && comp->len != ASN1_LEN_INDEF) { pri_message(pri, "!! Oversized NumberDigits component (%d)\n", comp->len); return -1; } if (comp->len == ASN1_LEN_INDEF) { datalen = strlen((char *)comp->data); res = datalen + 2; } else res = datalen = comp->len; memcpy(value->partyaddress, comp->data, datalen); value->partyaddress[datalen] = '\0'; return res + 2; } while(0); return -1; } static int rose_public_party_number_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) { int i = 0; struct rose_component *comp = NULL; unsigned char *vdata = data; int ton; int res = 0; if (len < 2) return -1; do { GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Don't know what to do with PublicPartyNumber ROSE component type 0x%x\n"); ASN1_GET_INTEGER(comp, ton); NEXT_COMPONENT(comp, i); ton = typeofnumber_for_q931(pri, ton); res = rose_number_digits_decode(pri, call, &vdata[i], len-i, value); if (res < 0) return -1; value->ton = ton; return res + 3; } while(0); return -1; } static int rose_address_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) { int i = 0; struct rose_component *comp = NULL; unsigned char *vdata = data; int res = 0; do { GET_COMPONENT(comp, i, vdata, len); switch(comp->type) { case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* [0] unknownPartyNumber */ res = rose_number_digits_decode(pri, call, comp->data, comp->len, value); if (res < 0) return -1; value->npi = PRI_NPI_UNKNOWN; value->ton = PRI_TON_UNKNOWN; break; case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0): /* [0] unknownPartyNumber */ res = asn1_copy_string(value->partyaddress, sizeof(value->partyaddress), comp); if (res < 0) return -1; value->npi = PRI_NPI_UNKNOWN; value->ton = PRI_TON_UNKNOWN; break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): /* [1] publicPartyNumber */ res = rose_public_party_number_decode(pri, call, comp->data, comp->len, value); if (res < 0) return -1; value->npi = PRI_NPI_E163_E164; break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2): /* [2] nsapEncodedNumber */ pri_message(pri, "!! NsapEncodedNumber isn't handled\n"); return -1; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): /* [3] dataPartyNumber */ if(rose_number_digits_decode(pri, call, comp->data, comp->len, value)) return -1; value->npi = PRI_NPI_X121 /* ??? */; value->ton = PRI_TON_UNKNOWN /* ??? */; pri_message(pri, "!! dataPartyNumber isn't handled\n"); return -1; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_4): /* [4] telexPartyNumber */ res = rose_number_digits_decode(pri, call, comp->data, comp->len, value); if (res < 0) return -1; value->npi = PRI_NPI_F69 /* ??? */; value->ton = PRI_TON_UNKNOWN /* ??? */; pri_message(pri, "!! telexPartyNumber isn't handled\n"); return -1; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_5): /* [5] priavePartyNumber */ pri_message(pri, "!! privatePartyNumber isn't handled\n"); value->npi = PRI_NPI_PRIVATE; return -1; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_8): /* [8] nationalStandardPartyNumber */ res = rose_number_digits_decode(pri, call, comp->data, comp->len, value); if (res < 0) return -1; value->npi = PRI_NPI_NATIONAL; value->ton = PRI_TON_NATIONAL; break; default: pri_message(pri, "!! Unknown Party number component received 0x%X\n", comp->type); return -1; } ASN1_FIXUP_LEN(comp, res); NEXT_COMPONENT(comp, i); if(i < len) pri_message(pri, "!! not all information is handled from Address component\n"); return res + 2; } while (0); return -1; } static int rose_presented_number_unscreened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) { int i = 0; int size = 0; struct rose_component *comp = NULL; unsigned char *vdata = data; /* Fill in default values */ value->ton = PRI_TON_UNKNOWN; value->npi = PRI_NPI_E163_E164; value->pres = -1; /* Data is not available */ do { GET_COMPONENT(comp, i, vdata, len); switch(comp->type) { case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* [0] presentationAllowedNumber */ value->pres = PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; size = rose_address_decode(pri, call, comp->data, comp->len, value); ASN1_FIXUP_LEN(comp, size); return size + 2; case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* [1] IMPLICIT presentationRestricted */ if (comp->len != 0) { /* must be NULL */ pri_error(pri, "!! Invalid PresentationRestricted component received (len != 0)\n"); return -1; } value->pres = PRES_PROHIB_USER_NUMBER_NOT_SCREENED; return 2; case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2): /* [2] IMPLICIT numberNotAvailableDueToInterworking */ if (comp->len != 0) { /* must be NULL */ pri_error(pri, "!! Invalid NumberNotAvailableDueToInterworking component received (len != 0)\n"); return -1; } value->pres = PRES_NUMBER_NOT_AVAILABLE; return 2; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): /* [3] presentationRestrictedNumber */ value->pres = PRES_PROHIB_USER_NUMBER_NOT_SCREENED; size = rose_address_decode(pri, call, comp->data, comp->len, value) + 2; ASN1_FIXUP_LEN(comp, size); return size + 2; default: pri_message(pri, "Invalid PresentedNumberUnscreened component 0x%X\n", comp->type); } return -1; } while (0); return -1; } static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *call, struct rose_component *sequence, int len) { int i = 0; int diversion_counter; int diversion_reason; char origcalledname[50] = "", redirectingname[50] = ""; struct addressingdataelements_presentednumberunscreened divertingnr; struct addressingdataelements_presentednumberunscreened originalcallednr; struct rose_component *comp = NULL; unsigned char *vdata = sequence->data; int res = 0; /* Data checks */ if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ pri_message(pri, "Invalid DivertingLegInformation2Type argument\n"); return -1; } if (sequence->len == ASN1_LEN_INDEF) { len -= 4; /* For the 2 extra characters at the end * and two characters of header */ } else len -= 2; do { /* diversionCounter stuff */ GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do it diversionCounter is of type 0x%x\n"); ASN1_GET_INTEGER(comp, diversion_counter); NEXT_COMPONENT(comp, i); /* diversionReason stuff */ GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Invalid diversionReason type 0x%X of ROSE divertingLegInformation2 component received\n"); ASN1_GET_INTEGER(comp, diversion_reason); NEXT_COMPONENT(comp, i); diversion_reason = redirectingreason_for_q931(pri, diversion_reason); if(pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Redirection reason: %d, total diversions: %d\n", diversion_reason, diversion_counter); pri_message(NULL, "Length of message is %d\n", len); for(; i < len; NEXT_COMPONENT(comp, i)) { GET_COMPONENT(comp, i, vdata, len); switch(comp->type) { case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0): call->origredirectingreason = redirectingreason_for_q931(pri, comp->data[0]); if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Received reason for original redirection %d\n", call->origredirectingreason); break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): res = rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &divertingnr); /* TODO: Fix indefinite length form hacks */ ASN1_FIXUP_LEN(comp, res); comp->len = res; if (res < 0) return -1; if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, " Received divertingNr '%s'\n", divertingnr.partyaddress); pri_message(pri, " ton = %d, pres = %d, npi = %d\n", divertingnr.ton, divertingnr.pres, divertingnr.npi); } break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2): res = rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &originalcallednr); if (res < 0) return -1; ASN1_FIXUP_LEN(comp, res); comp->len = res; if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, " Received originalcallednr '%s'\n", originalcallednr.partyaddress); pri_message(pri, " ton = %d, pres = %d, npi = %d\n", originalcallednr.ton, originalcallednr.pres, originalcallednr.npi); } break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): res = asn1_name_decode(comp->data, comp->len, redirectingname, sizeof(redirectingname)); if (res < 0) return -1; ASN1_FIXUP_LEN(comp, res); comp->len = res; if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Received RedirectingName '%s'\n", redirectingname); break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_4): res = asn1_name_decode(comp->data, comp->len, origcalledname, sizeof(origcalledname)); if (res < 0) return -1; ASN1_FIXUP_LEN(comp, res); comp->len = res; if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Received Originally Called Name '%s'\n", origcalledname); break; default: if (comp->type == 0 && comp->len == 0) { break; /* Found termination characters */ } pri_message(pri, "!! Invalid DivertingLegInformation2 component received 0x%X\n", comp->type); return -1; } } if (divertingnr.pres >= 0) { call->redirectingplan = divertingnr.npi; call->redirectingpres = divertingnr.pres; call->redirectingreason = diversion_reason; libpri_copy_string(call->redirectingnum, divertingnr.partyaddress, sizeof(call->redirectingnum)); } if (originalcallednr.pres >= 0) { call->origcalledplan = originalcallednr.npi; call->origcalledpres = originalcallednr.pres; libpri_copy_string(call->origcallednum, originalcallednr.partyaddress, sizeof(call->origcallednum)); } libpri_copy_string(call->redirectingname, redirectingname, sizeof(call->redirectingname)); libpri_copy_string(call->origcalledname, origcalledname, sizeof(call->origcalledname)); return 0; } while (0); return -1; } static int rose_diverting_leg_information2_encode(struct pri *pri, q931_call *call) { int i = 0, j, compsp = 0; struct rose_component *comp, *compstk[10]; unsigned char buffer[256]; int len = 253; #if 0 /* This is not required by specifications */ if (!strlen(call->callername)) { return -1; } #endif buffer[i] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); i++; /* Interpretation component */ ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0x00 /* Discard unrecognized invokes */); ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Invoke component contents */ /* Invoke ID */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); /* Operation Tag */ /* ROSE operationId component */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_DIVERTING_LEG_INFORMATION2); /* ROSE ARGUMENT component */ ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* ROSE DivertingLegInformation2.diversionCounter component */ /* Always is 1 because other isn't available in the current design */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, 1); /* ROSE DivertingLegInformation2.diversionReason component */ ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, redirectingreason_from_q931(pri, call->redirectingreason)); /* ROSE DivertingLegInformation2.divertingNr component */ ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Redirecting information always not screened */ switch(call->redirectingpres) { case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: if (call->redirectingnum && strlen(call->redirectingnum)) { ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* NPI of redirected number is not supported in the current design */ ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4)); j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum)); if (j < 0) return -1; i += j; ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); break; } /* fall through */ case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); break; /* Don't know how to handle this */ case PRES_ALLOWED_NETWORK_NUMBER: case PRES_PROHIB_NETWORK_NUMBER: case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); break; default: pri_message(pri, "!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres); case PRES_NUMBER_NOT_AVAILABLE: ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i); break; } ASN1_FIXUP(compstk, compsp, buffer, i); /* ROSE DivertingLegInformation2.originalCalledNr component */ /* This information isn't supported by current design - duplicate divertingNr */ ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Redirecting information always not screened */ switch(call->redirectingpres) { case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: if (call->redirectingnum && strlen(call->redirectingnum)) { ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0), buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4)); j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum)); if (j < 0) return -1; i += j; ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); break; } /* fall through */ case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); break; /* Don't know how to handle this */ case PRES_ALLOWED_NETWORK_NUMBER: case PRES_PROHIB_NETWORK_NUMBER: case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); break; default: pri_message(pri, "!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres); case PRES_NUMBER_NOT_AVAILABLE: ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i); break; } ASN1_FIXUP(compstk, compsp, buffer, i); /* Fix length of stacked components */ while(compsp > 0) { ASN1_FIXUP(compstk, compsp, buffer, i); } if (pri_call_apdu_queue(call, Q931_SETUP, buffer, i, NULL, NULL)) return -1; return 0; } /* Send the rltThirdParty: Invoke */ int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) { int i = 0; unsigned char buffer[256]; struct rose_component *comp = NULL, *compstk[10]; const unsigned char rlt_3rd_pty = RLT_THIRD_PARTY; q931_call *callwithid = NULL, *apdubearer = NULL; int compsp = 0; if (c2->transferable) { apdubearer = c1; callwithid = c2; } else if (c1->transferable) { apdubearer = c2; callwithid = c1; } else return -1; buffer[i++] = (Q932_PROTOCOL_ROSE); buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Invoke ID is set to the operation ID */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_3rd_pty); /* Operation Tag */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_3rd_pty); /* Additional RLT invoke info - Octet 12 */ ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_WORDCOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, callwithid->rlt_call_id & 0xFFFFFF); /* Length is 3 octets */ /* Reason for redirect - unused, set to 129 */ ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i, 0); ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, i, NULL, NULL)) return -1; if (q931_facility(apdubearer->pri, apdubearer)) { pri_message(pri, "Could not schedule facility message for call %d\n", apdubearer->cr); return -1; } return 0; } static int add_dms100_transfer_ability_apdu(struct pri *pri, q931_call *c) { int i = 0; unsigned char buffer[256]; struct rose_component *comp = NULL, *compstk[10]; const unsigned char rlt_op_ind = RLT_OPERATION_IND; int compsp = 0; buffer[i++] = (Q932_PROTOCOL_ROSE); /* Note to self: DON'T set the EXT bit */ buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Invoke ID is set to the operation ID */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_op_ind); /* Operation Tag - basically the same as the invoke ID tag */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_op_ind); ASN1_FIXUP(compstk, compsp, buffer, i); if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) return -1; else return 0; } /* Sending callername information functions */ static int add_callername_facility_ies(struct pri *pri, q931_call *c, int cpe) { int res = 0; int i = 0; unsigned char buffer[256]; unsigned char namelen = 0; struct rose_component *comp = NULL, *compstk[10]; int compsp = 0; int mymessage = 0; static unsigned char op_tag[] = { 0x2a, /* informationFollowing 42 */ 0x86, 0x48, 0xce, 0x15, 0x00, 0x04 }; if (!strlen(c->callername)) { return -1; } buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); /* Interpretation component */ if (pri->switchtype == PRI_SWITCH_QSIG) { ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); ASN1_FIXUP(compstk, compsp, buffer, i); } ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Invoke ID */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); /* Operation Tag */ res = asn1_string_encode(ASN1_OBJECTIDENTIFIER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); if (res < 0) return -1; i += res; ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 0); ASN1_FIXUP(compstk, compsp, buffer, i); if (!cpe) { if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) return -1; } /* Now the APDU that contains the information that needs sent. * We can reuse the buffer since the queue function doesn't * need it. */ i = 0; namelen = strlen(c->callername); if (namelen > 50) { namelen = 50; /* truncate the name */ } buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); /* Interpretation component */ if (pri->switchtype == PRI_SWITCH_QSIG) { ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); ASN1_FIXUP(compstk, compsp, buffer, i); } ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); /* Invoke ID */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); /* Operation ID: Calling name */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, SS_CNID_CALLINGNAME); res = asn1_string_encode((ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), &buffer[i], sizeof(buffer)-i, 50, c->callername, namelen); if (res < 0) return -1; i += res; ASN1_FIXUP(compstk, compsp, buffer, i); if (cpe) mymessage = Q931_SETUP; else mymessage = Q931_FACILITY; if (pri_call_apdu_queue(c, mymessage, buffer, i, NULL, NULL)) return -1; return 0; } /* End Callername */ /* MWI related encode and decode functions */ static void mwi_activate_encode_cb(void *data) { return; } int mwi_message_send(struct pri* pri, q931_call *call, struct pri_sr *req, int activate) { int i = 0; unsigned char buffer[255] = ""; int destlen = strlen(req->called); struct rose_component *comp = NULL, *compstk[10]; int compsp = 0; int res; if (destlen <= 0) { return -1; } else if (destlen > 20) destlen = 20; /* Destination number cannot be greater then 20 digits */ buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); /* Interpretation component */ ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, (activate) ? SS_MWI_ACTIVATE : SS_MWI_DEACTIVATE); ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* PartyNumber */ res = asn1_string_encode((ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), &buffer[i], sizeof(buffer)-i, destlen, req->called, destlen); if (res < 0) return -1; i += res; /* Enumeration: basicService */ ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 1 /* contents: Voice */); ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); return pri_call_apdu_queue(call, Q931_SETUP, buffer, i, mwi_activate_encode_cb, NULL); } /* End MWI */ /* EECT functions */ int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) { int i = 0; int res = 0; unsigned char buffer[255] = ""; short call_reference = c2->cr ^ 0x8000; /* Let's do the trickery to make sure the flag is correct */ struct rose_component *comp = NULL, *compstk[10]; int compsp = 0; static unsigned char op_tag[] = { 0x2A, 0x86, 0x48, 0xCE, 0x15, 0x00, 0x08, }; buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_ROSE); ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); res = asn1_string_encode(ASN1_OBJECTIDENTIFIER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); if (res < 0) return -1; i += res; ASN1_ADD_SIMPLE(comp, (ASN1_SEQUENCE | ASN1_CONSTRUCTOR), buffer, i); ASN1_PUSH(compstk, compsp, comp); ASN1_ADD_WORDCOMP(comp, ASN1_INTEGER, buffer, i, call_reference); ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, i, NULL, NULL); if (res) { pri_message(pri, "Could not queue APDU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ res = q931_facility(c1->pri, c1); if (res) { pri_message(pri, "Could not schedule facility message for call %d\n", c1->cr); return -1; } return 0; } /* End EECT */ /* AOC */ static int aoc_aoce_charging_request_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int chargingcase = -1; unsigned char *vdata = data; struct rose_component *comp = NULL; int pos1 = 0; if (pri->debug & PRI_DEBUG_AOC) dump_apdu (pri, data, len); do { GET_COMPONENT(comp, pos1, vdata, len); CHECK_COMPONENT(comp, ASN1_ENUMERATED, "!! Invalid AOC Charging Request argument. Expected Enumerated (0x0A) but Received 0x%02X\n"); ASN1_GET_INTEGER(comp, chargingcase); if (chargingcase >= 0 && chargingcase <= 2) { if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Channel %d/%d, Call %d - received AOC charging request - charging case: %i\n", call->ds1no, call->channelno, call->cr, chargingcase); } else { pri_message(pri, "!! unkown AOC ChargingCase: 0x%02X", chargingcase); chargingcase = -1; } NEXT_COMPONENT(comp, pos1); } while (pos1 < len); if (pos1 < len) { pri_message(pri, "!! Only reached position %i in %i bytes long AOC-E structure:", pos1, len ); dump_apdu (pri, data, len); return -1; /* Aborted before */ } return 0; } static int aoc_aoce_charging_unit_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { long chargingunits = 0, chargetype = -1, temp, chargeIdentifier = -1; unsigned char *vdata = data; struct rose_component *comp1 = NULL, *comp2 = NULL, *comp3 = NULL; int pos1 = 0, pos2, pos3, sublen2, sublen3; struct addressingdataelements_presentednumberunscreened chargednr; if (pri->debug & PRI_DEBUG_AOC) dump_apdu (pri, data, len); do { GET_COMPONENT(comp1, pos1, vdata, len); /* AOCEChargingUnitInfo */ CHECK_COMPONENT(comp1, ASN1_SEQUENCE, "!! Invalid AOC-E Charging Unit argument. Expected Sequence (0x30) but Received 0x%02X\n"); SUB_COMPONENT(comp1, pos1); GET_COMPONENT(comp1, pos1, vdata, len); switch (comp1->type) { case (ASN1_SEQUENCE | ASN1_CONSTRUCTOR): /* specificChargingUnits */ sublen2 = comp1->len; pos2 = pos1; comp2 = comp1; SUB_COMPONENT(comp2, pos2); do { GET_COMPONENT(comp2, pos2, vdata, len); switch (comp2->type) { case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): /* RecordedUnitsList (0xA1) */ SUB_COMPONENT(comp2, pos2); GET_COMPONENT(comp2, pos2, vdata, len); CHECK_COMPONENT(comp2, ASN1_SEQUENCE, "!! Invalid AOC-E Charging Unit argument. Expected Sequence (0x30) but received 0x02%X\n"); /* RecordedUnits */ sublen3 = pos2 + comp2->len; pos3 = pos2; comp3 = comp2; SUB_COMPONENT(comp3, pos3); do { GET_COMPONENT(comp3, pos3, vdata, len); switch (comp3->type) { case ASN1_INTEGER: /* numberOfUnits */ ASN1_GET_INTEGER(comp3, temp); chargingunits += temp; case ASN1_NULL: /* notAvailable */ break; default: pri_message(pri, "!! Don't know how to handle 0x%02X in AOC-E RecordedUnits\n", comp3->type); } NEXT_COMPONENT(comp3, pos3); } while (pos3 < sublen3); if (pri->debug & PRI_DEBUG_AOC) pri_message(pri, "Channel %d/%d, Call %d - received AOC-E charging: %i unit%s\n", call->ds1no, call->channelno, call->cr, chargingunits, (chargingunits == 1) ? "" : "s"); break; case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2): /* AOCEBillingID (0xA2) */ SUB_COMPONENT(comp2, pos2); GET_COMPONENT(comp2, pos2, vdata, len); ASN1_GET_INTEGER(comp2, chargetype); pri_message(pri, "!! not handled: Channel %d/%d, Call %d - received AOC-E billing ID: %i\n", call->ds1no, call->channelno, call->cr, chargetype); break; default: pri_message(pri, "!! Don't know how to handle 0x%02X in AOC-E RecordedUnitsList\n", comp2->type); } NEXT_COMPONENT(comp2, pos2); } while (pos2 < sublen2); break; case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* freeOfCharge (0x81) */ if (pri->debug & PRI_DEBUG_AOC) pri_message(pri, "Channel %d/%d, Call %d - received AOC-E free of charge\n", call->ds1no, call->channelno, call->cr); chargingunits = 0; break; default: pri_message(pri, "!! Invalid AOC-E specificChargingUnits. Expected Sequence (0x30) or Object Identifier (0x81/0x01) but received 0x%02X\n", comp1->type); } NEXT_COMPONENT(comp1, pos1); GET_COMPONENT(comp1, pos1, vdata, len); /* get optional chargingAssociation. will 'break' when reached end of structure */ switch (comp1->type) { /* TODO: charged number is untested - please report! */ case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* chargedNumber (0xA0) */ if(rose_presented_number_unscreened_decode(pri, call, comp1->data, comp1->len, &chargednr) != 0) return -1; pri_message(pri, "!! not handled: Received ChargedNr '%s' \n", chargednr.partyaddress); pri_message(pri, " ton = %d, pres = %d, npi = %d\n", chargednr.ton, chargednr.pres, chargednr.npi); break; case ASN1_INTEGER: ASN1_GET_INTEGER(comp1, chargeIdentifier); break; default: pri_message(pri, "!! Invalid AOC-E chargingAssociation. Expected Object Identifier (0xA0) or Integer (0x02) but received 0x%02X\n", comp1->type); } NEXT_COMPONENT(comp1, pos1); } while (pos1 < len); if (pos1 < len) { pri_message(pri, "!! Only reached position %i in %i bytes long AOC-E structure:", pos1, len ); dump_apdu (pri, data, len); return -1; /* oops - aborted before */ } call->aoc_units = chargingunits; return 0; } static int aoc_aoce_charging_unit_encode(struct pri *pri, q931_call *c, long chargedunits) { /* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */ int i = 0, res = 0, compsp = 0; unsigned char buffer[255] = ""; struct rose_component *comp = NULL, *compstk[10]; /* ROSE protocol (0x91)*/ buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_ROSE); /* ROSE Component (0xA1,len)*/ ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); ASN1_PUSH(compstk, compsp, comp); /* ROSE invokeId component (0x02,len,id)*/ ASN1_ADD_WORDCOMP(comp, INVOKE_IDENTIFIER, buffer, i, ++pri->last_invoke); /* ROSE operationId component (0x02,0x01,0x24)*/ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_AOC_AOCE_CHARGING_UNIT); /* AOCEChargingUnitInfo (0x30,len) */ ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); ASN1_PUSH(compstk, compsp, comp); if (chargedunits > 0) { /* SpecificChargingUnits (0x30,len) */ ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* RecordedUnitsList (0xA1,len) */ ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* RecordedUnits (0x30,len) */ ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); ASN1_PUSH(compstk, compsp, comp); /* NumberOfUnits (0x02,len,charge) */ ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, chargedunits); ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); } else { /* freeOfCharge (0x81,0) */ ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); } ASN1_FIXUP(compstk, compsp, buffer, i); ASN1_FIXUP(compstk, compsp, buffer, i); if (pri->debug & PRI_DEBUG_AOC) dump_apdu (pri, buffer, i); /* code below is untested */ res = pri_call_apdu_queue(c, Q931_FACILITY, buffer, i, NULL, NULL); if (res) { pri_message(pri, "Could not queue APDU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ res = q931_facility(c->pri, c); if (res) { pri_message(pri, "Could not schedule facility message for call %d\n", c->cr); return -1; } return 0; } /* End AOC */ int rose_reject_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; int problemtag = -1; int problem = -1; int invokeidvalue = -1; unsigned char *vdata = data; struct rose_component *comp = NULL; char *problemtagstr, *problemstr; do { /* Invoke ID stuff */ GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); ASN1_GET_INTEGER(comp, invokeidvalue); NEXT_COMPONENT(comp, i); GET_COMPONENT(comp, i, vdata, len); problemtag = comp->type; problem = comp->data[0]; if (pri->switchtype == PRI_SWITCH_DMS100) { switch (problemtag) { case 0x80: problemtagstr = "General problem"; break; case 0x81: problemtagstr = "Invoke problem"; break; case 0x82: problemtagstr = "Return result problem"; break; case 0x83: problemtagstr = "Return error problem"; break; default: problemtagstr = "Unknown"; } switch (problem) { case 0x00: problemstr = "Unrecognized component"; break; case 0x01: problemstr = "Mistyped component"; break; case 0x02: problemstr = "Badly structured component"; break; default: problemstr = "Unknown"; } pri_error(pri, "ROSE REJECT:\n"); pri_error(pri, "\tINVOKE ID: 0x%X\n", invokeidvalue); pri_error(pri, "\tPROBLEM TYPE: %s (0x%x)\n", problemtagstr, problemtag); pri_error(pri, "\tPROBLEM: %s (0x%x)\n", problemstr, problem); return 0; } else { pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); return -1; } } while(0); return -1; } int rose_return_error_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; int errorvalue = -1; int invokeidvalue = -1; unsigned char *vdata = data; struct rose_component *comp = NULL; char *invokeidstr, *errorstr; do { /* Invoke ID stuff */ GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); ASN1_GET_INTEGER(comp, invokeidvalue); NEXT_COMPONENT(comp, i); GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second component in return error is 0x%x\n"); ASN1_GET_INTEGER(comp, errorvalue); if (pri->switchtype == PRI_SWITCH_DMS100) { switch (invokeidvalue) { case RLT_OPERATION_IND: invokeidstr = "RLT_OPERATION_IND"; break; case RLT_THIRD_PARTY: invokeidstr = "RLT_THIRD_PARTY"; break; default: invokeidstr = "Unknown"; } switch (errorvalue) { case 0x10: errorstr = "RLT Bridge Fail"; break; case 0x11: errorstr = "RLT Call ID Not Found"; break; case 0x12: errorstr = "RLT Not Allowed"; break; case 0x13: errorstr = "RLT Switch Equip Congs"; break; default: errorstr = "Unknown"; } pri_error(pri, "ROSE RETURN ERROR:\n"); pri_error(pri, "\tOPERATION: %s\n", invokeidstr); pri_error(pri, "\tERROR: %s\n", errorstr); return 0; } else { pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); return -1; } } while(0); return -1; } int rose_return_result_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; int operationidvalue = -1; int invokeidvalue = -1; unsigned char *vdata = data; struct rose_component *comp = NULL; do { /* Invoke ID stuff */ GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); ASN1_GET_INTEGER(comp, invokeidvalue); NEXT_COMPONENT(comp, i); if (pri->switchtype == PRI_SWITCH_DMS100) { switch (invokeidvalue) { case RLT_THIRD_PARTY: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Successfully completed RLT transfer!\n"); return 0; case RLT_OPERATION_IND: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Received RLT_OPERATION_IND\n"); /* Have to take out the rlt_call_id */ GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_SEQUENCE, "Protocol error detected in parsing RLT_OPERATION_IND return result!\n"); /* Traverse the contents of this sequence */ /* First is the Operation Value */ SUB_COMPONENT(comp, i); GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_INTEGER, "RLT_OPERATION_IND should be of type ASN1_INTEGER!\n"); ASN1_GET_INTEGER(comp, operationidvalue); if (operationidvalue != RLT_OPERATION_IND) { pri_message(pri, "Invalid Operation ID value (0x%x) in return result!\n", operationidvalue); return -1; } /* Next is the Call ID */ NEXT_COMPONENT(comp, i); GET_COMPONENT(comp, i, vdata, len); CHECK_COMPONENT(comp, ASN1_TAG_0, "Error check failed on Call ID!\n"); ASN1_GET_INTEGER(comp, call->rlt_call_id); /* We have enough data to transfer the call */ call->transferable = 1; return 0; default: pri_message(pri, "Could not parse invoke of type 0x%x!\n", invokeidvalue); return -1; } } else { pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); return -1; } } while(0); return -1; } int rose_invoke_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; int operation_tag; unsigned char *vdata = data; struct rose_component *comp = NULL, *invokeid = NULL, *operationid = NULL; do { /* Invoke ID stuff */ GET_COMPONENT(comp, i, vdata, len); #if 0 CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); #endif invokeid = comp; NEXT_COMPONENT(comp, i); /* Operation Tag */ GET_COMPONENT(comp, i, vdata, len); #if 0 CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second ROSE component is of type 0x%x\n"); #endif operationid = comp; ASN1_GET_INTEGER(comp, operation_tag); NEXT_COMPONENT(comp, i); /* No argument - return with error */ if (i >= len) return -1; /* Arguement Tag */ GET_COMPONENT(comp, i, vdata, len); if (!comp->type) return -1; if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " [ Handling operation %d ]\n", operation_tag); switch (operation_tag) { case SS_CNID_CALLINGNAME: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Handle Name display operation\n"); switch (comp->type) { case ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE: memcpy(call->callername, comp->data, comp->len); call->callername[comp->len] = 0; if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Received caller name '%s'\n", call->callername); return 0; default: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Do not handle argument of type 0x%X\n", comp->type); return -1; } break; case ROSE_DIVERTING_LEG_INFORMATION2: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, " Handle DivertingLegInformation2\n"); return rose_diverting_leg_information2_decode(pri, call, comp, len-i); case ROSE_AOC_NO_CHARGING_INFO_AVAILABLE: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC No Charging Info Available - not handled!", operation_tag); dump_apdu (pri, comp->data, comp->len); } return -1; case ROSE_AOC_CHARGING_REQUEST: return aoc_aoce_charging_request_decode(pri, call, (u_int8_t *)comp, comp->len + 2); case ROSE_AOC_AOCS_CURRENCY: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC-S Currency - not handled!", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; case ROSE_AOC_AOCS_SPECIAL_ARR: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC-S Special Array - not handled!", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; case ROSE_AOC_AOCD_CURRENCY: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC-D Currency - not handled!", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; case ROSE_AOC_AOCD_CHARGING_UNIT: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC-D Charging Unit - not handled!", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; case ROSE_AOC_AOCE_CURRENCY: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC-E Currency - not handled!", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; case ROSE_AOC_AOCE_CHARGING_UNIT: return aoc_aoce_charging_unit_decode(pri, call, (u_int8_t *)comp, comp->len + 2); if (0) { /* the following function is currently not used - just to make the compiler happy */ aoc_aoce_charging_unit_encode(pri, call, call->aoc_units); /* use this function to forward the aoc-e on a bridged channel */ return 0; } case ROSE_AOC_IDENTIFICATION_OF_CHARGE: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "ROSE %i: AOC Identification Of Charge - not handled!", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; default: if (pri->debug & PRI_DEBUG_APDU) { pri_message(pri, "!! Unable to handle ROSE operation %d", operation_tag); dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); } return -1; } } while(0); return -1; } int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data) { struct apdu_event *cur = NULL; struct apdu_event *new_event = NULL; if (!call || !messagetype || !apdu || (apdu_len < 1) || (apdu_len > 255)) return -1; new_event = malloc(sizeof(struct apdu_event)); if (new_event) { memset(new_event, 0, sizeof(struct apdu_event)); new_event->message = messagetype; new_event->callback = function; new_event->data = data; memcpy(new_event->apdu, apdu, apdu_len); new_event->apdu_len = apdu_len; } else { pri_error(call->pri, "!! Malloc failed!\n"); return -1; } if (call->apdus) { cur = call->apdus; while (cur->next) { cur = cur->next; } cur->next = new_event; } else call->apdus = new_event; return 0; } int pri_call_apdu_queue_cleanup(q931_call *call) { struct apdu_event *cur_event = NULL, *free_event = NULL; if (call && call->apdus) { cur_event = call->apdus; while (cur_event) { /* TODO: callbacks, some way of giving return res on status of apdu */ free_event = cur_event; cur_event = cur_event->next; free(free_event); } call->apdus = NULL; } return 0; } int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) { if (!pri->sendfacility) return 0; if (pri->switchtype == PRI_SWITCH_QSIG) { /* For Q.SIG it does network and cpe operations */ if (call->redirectingnum[0]) rose_diverting_leg_information2_encode(pri, call); add_callername_facility_ies(pri, call, 1); return 0; } #if 0 if (pri->localtype == PRI_NETWORK) { switch (pri->switchtype) { case PRI_SWITCH_NI2: add_callername_facility_ies(pri, call, 0); break; default: break; } return 0; } else if (pri->localtype == PRI_CPE) { switch (pri->switchtype) { case PRI_SWITCH_NI2: add_callername_facility_ies(pri, call, 1); break; default: break; } return 0; } #else if (pri->switchtype == PRI_SWITCH_NI2) add_callername_facility_ies(pri, call, (pri->localtype == PRI_CPE)); #endif if ((pri->switchtype == PRI_SWITCH_DMS100) && (pri->localtype == PRI_CPE)) { add_dms100_transfer_ability_apdu(pri, call); } return 0; } libpri-1.4.3/pri_q931.h0000644000000000000000000002103510453276211013220 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Linux Support Services, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _PRI_Q931_H #define _PRI_Q931_H typedef enum q931_state { /* User states */ U0_NULL_STATE, U1_CALL_INITIATED, U2_OVERLAP_SENDING, U3_OUTGOING_CALL_PROCEEDING, U4_CALL_DELIVERED, U6_CALL_PRESENT, U7_CALL_RECEIVED, U8_CONNECT_REQUEST, U9_INCOMING_CALL_PROCEEDING, U10_ACTIVE, U11_DISCONNECT_REQUEST, U12_DISCONNECT_INDICATION, U15_SUSPEND_REQUEST, U17_RESUME_REQUEST, U19_RELEASE_REQUEST, U25_OVERLAP_RECEIVING, /* Network states */ N0_NULL_STATE, N1_CALL_INITIATED, N2_OVERLAP_SENDING, N3_OUTGOING_CALL_PROCEEDING, N4_CALL_DELIVERED, N6_CALL_PRESENT, N7_CALL_RECEIVED, N8_CONNECT_REQUEST, N9_INCOMING_CALL_PROCEEDING, N10_ACTIVE, N11_DISCONNECT_REQUEST, N12_DISCONNECT_INDICATION, N15_SUSPEND_REQUEST, N17_RESUME_REQUEST, N19_RELEASE_REQUEST, N22_CALL_ABORT, N25_OVERLAP_RECEIVING } q931_state; typedef enum q931_mode { UNKNOWN_MODE, CIRCUIT_MODE, PACKET_MODE } q931_mode; typedef struct q931_h { unsigned char raw[0]; u_int8_t pd; /* Protocol Discriminator */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t x0:4; u_int8_t crlen:4; #else u_int8_t crlen:4; u_int8_t x0:4; #endif u_int8_t contents[0]; u_int8_t crv[3]; } __attribute__ ((packed)) q931_h; /* Message type header */ typedef struct q931_mh { #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t f:1; u_int8_t msg:7; #else u_int8_t msg:7; u_int8_t f:1; #endif u_int8_t data[0]; } __attribute__ ((packed)) q931_mh; /* Information element format */ typedef struct q931_ie { u_int8_t ie; u_int8_t len; u_int8_t data[0]; } __attribute__ ((packed)) q931_ie; #define Q931_RES_HAVEEVENT (1 << 0) #define Q931_RES_INERRROR (1 << 1) #define Q931_PROTOCOL_DISCRIMINATOR 0x08 #define GR303_PROTOCOL_DISCRIMINATOR 0x4f /* Q.931 / National ISDN Message Types */ /* Call Establishment Messages */ #define Q931_ALERTING 0x01 #define Q931_CALL_PROCEEDING 0x02 #define Q931_CONNECT 0x07 #define Q931_CONNECT_ACKNOWLEDGE 0x0f #define Q931_PROGRESS 0x03 #define Q931_SETUP 0x05 #define Q931_SETUP_ACKNOWLEDGE 0x0d /* Call Disestablishment Messages */ #define Q931_DISCONNECT 0x45 #define Q931_RELEASE 0x4d #define Q931_RELEASE_COMPLETE 0x5a #define Q931_RESTART 0x46 #define Q931_RESTART_ACKNOWLEDGE 0x4e /* Miscellaneous Messages */ #define Q931_STATUS 0x7d #define Q931_STATUS_ENQUIRY 0x75 #define Q931_USER_INFORMATION 0x20 #define Q931_SEGMENT 0x60 #define Q931_CONGESTION_CONTROL 0x79 #define Q931_INFORMATION 0x7b #define Q931_FACILITY 0x62 #define Q931_NOTIFY 0x6e /* Call Management Messages */ #define Q931_HOLD 0x24 #define Q931_HOLD_ACKNOWLEDGE 0x28 #define Q931_HOLD_REJECT 0x30 #define Q931_RETRIEVE 0x31 #define Q931_RETRIEVE_ACKNOWLEDGE 0x33 #define Q931_RETRIEVE_REJECT 0x37 #define Q931_RESUME 0x26 #define Q931_RESUME_ACKNOWLEDGE 0x2e #define Q931_RESUME_REJECT 0x22 #define Q931_SUSPEND 0x25 #define Q931_SUSPEND_ACKNOWLEDGE 0x2d #define Q931_SUSPEND_REJECT 0x21 /* Maintenance messages (codeset 0 only) */ #define NATIONAL_SERVICE 0x0f #define NATIONAL_SERVICE_ACKNOWLEDGE 0x07 /* Special codeset 0 IE */ #define NATIONAL_CHANGE_STATUS 0x1 /* Q.931 / National ISDN Information Elements */ #define Q931_LOCKING_SHIFT 0x90 #define Q931_NON_LOCKING_SHIFT 0x98 #define Q931_BEARER_CAPABILITY 0x04 #define Q931_CAUSE 0x08 #define Q931_CALL_STATE 0x14 #define Q931_CHANNEL_IDENT 0x18 #define Q931_PROGRESS_INDICATOR 0x1e #define Q931_NETWORK_SPEC_FAC 0x20 #define Q931_INFORMATION_RATE 0x40 #define Q931_TRANSIT_DELAY 0x42 #define Q931_TRANS_DELAY_SELECT 0x43 #define Q931_BINARY_PARAMETERS 0x44 #define Q931_WINDOW_SIZE 0x45 #define Q931_PACKET_SIZE 0x46 #define Q931_CLOSED_USER_GROUP 0x47 #define Q931_REVERSE_CHARGE_INDIC 0x4a #define Q931_CALLING_PARTY_NUMBER 0x6c #define Q931_CALLING_PARTY_SUBADDR 0x6d #define Q931_CALLED_PARTY_NUMBER 0x70 #define Q931_CALLED_PARTY_SUBADDR 0x71 #define Q931_REDIRECTING_NUMBER 0x74 #define Q931_REDIRECTING_SUBADDR 0x75 #define Q931_TRANSIT_NET_SELECT 0x78 #define Q931_RESTART_INDICATOR 0x79 #define Q931_LOW_LAYER_COMPAT 0x7c #define Q931_HIGH_LAYER_COMPAT 0x7d #define Q931_CODESET(x) ((x) << 8) #define Q931_IE_CODESET(x) ((x) >> 8) #define Q931_IE_IE(x) ((x) & 0xff) #define Q931_FULL_IE(codeset, ie) (((codeset) << 8) | ((ie) & 0xff)) #define Q931_DISPLAY 0x28 #define Q931_IE_SEGMENTED_MSG 0x00 #define Q931_IE_CHANGE_STATUS 0x01 #define Q931_IE_ORIGINATING_LINE_INFO (0x01 | Q931_CODESET(6)) #define Q931_IE_CONNECTED_ADDR 0x0C #define Q931_IE_CONNECTED_NUM 0x4C #define Q931_IE_CALL_IDENTITY 0x10 #define Q931_IE_FACILITY 0x1c #define Q931_IE_ENDPOINT_ID 0x26 #define Q931_IE_NOTIFY_IND 0x27 #define Q931_IE_TIME_DATE 0x29 #define Q931_IE_KEYPAD_FACILITY 0x2c #define Q931_IE_CALL_STATUS 0x2d #define Q931_IE_UPDATE 0x31 #define Q931_IE_INFO_REQUEST 0x32 #define Q931_IE_SIGNAL 0x34 #define Q931_IE_SWITCHHOOK 0x36 #define Q931_IE_GENERIC_DIGITS (0x37 | Q931_CODESET(6)) #define Q931_IE_FEATURE_ACTIVATE 0x38 #define Q931_IE_FEATURE_IND 0x39 #define Q931_IE_ORIGINAL_CALLED_NUMBER 0x73 #define Q931_IE_REDIRECTION_NUMBER 0x76 #define Q931_IE_REDIRECTION_SUBADDR 0x77 #define Q931_IE_USER_USER_FACILITY 0x7A #define Q931_IE_USER_USER 0x7E #define Q931_IE_ESCAPE_FOR_EXT 0x7F /* Call state stuff */ #define Q931_CALL_STATE_NULL 0 #define Q931_CALL_STATE_CALL_INITIATED 1 #define Q931_CALL_STATE_OVERLAP_SENDING 2 #define Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING 3 #define Q931_CALL_STATE_CALL_DELIVERED 4 #define Q931_CALL_STATE_CALL_PRESENT 6 #define Q931_CALL_STATE_CALL_RECEIVED 7 #define Q931_CALL_STATE_CONNECT_REQUEST 8 #define Q931_CALL_STATE_INCOMING_CALL_PROCEEDING 9 #define Q931_CALL_STATE_ACTIVE 10 #define Q931_CALL_STATE_DISCONNECT_REQUEST 11 #define Q931_CALL_STATE_DISCONNECT_INDICATION 12 #define Q931_CALL_STATE_SUSPEND_REQUEST 15 #define Q931_CALL_STATE_RESUME_REQUEST 17 #define Q931_CALL_STATE_RELEASE_REQUEST 19 #define Q931_CALL_STATE_OVERLAP_RECEIVING 25 #define Q931_CALL_STATE_RESTART_REQUEST 61 #define Q931_CALL_STATE_RESTART 62 /* EuroISDN */ #define Q931_SENDING_COMPLETE 0xa1 /* Q.SIG specific */ #define QSIG_IE_TRANSIT_COUNT 0x31 extern int q931_receive(struct pri *pri, q931_h *h, int len); extern int q931_alerting(struct pri *pri, q931_call *call, int channel, int info); extern int q931_call_progress(struct pri *pri, q931_call *call, int channel, int info); extern int q931_notify(struct pri *pri, q931_call *call, int channel, int info); extern int q931_call_proceeding(struct pri *pri, q931_call *call, int channel, int info); extern int q931_setup_ack(struct pri *pri, q931_call *call, int channel, int nonisdn); extern int q931_information(struct pri *pri, q931_call *call, char digit); extern int q931_keypad_facility(struct pri *pri, q931_call *call, char *digits); extern int q931_connect(struct pri *pri, q931_call *call, int channel, int nonisdn); extern int q931_release(struct pri *pri, q931_call *call, int cause); extern int q931_disconnect(struct pri *pri, q931_call *call, int cause); extern int q931_hangup(struct pri *pri, q931_call *call, int cause); extern int q931_restart(struct pri *pri, int channel); extern int q931_facility(struct pri *pri, q931_call *call); extern int q931_call_getcrv(struct pri *pri, q931_call *call, int *callmode); extern int q931_call_setcrv(struct pri *pri, q931_call *call, int crv, int callmode); extern q931_call *q931_new_call(struct pri *pri); extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req); extern void q931_dump(struct pri *pri, q931_h *h, int len, int txrx); extern void __q931_destroycall(struct pri *pri, q931_call *c); extern void q931_dl_indication(struct pri *pri, int event); #endif libpri-1.4.3/mkdep0000755000000000000000000000651010430050503012514 0ustar rootroot#!/bin/sh - # # $OpenBSD: mkdep.gcc.sh,v 1.8 1998/09/02 06:40:07 deraadt Exp $ # $NetBSD: mkdep.gcc.sh,v 1.9 1994/12/23 07:34:59 jtc Exp $ # # Copyright (c) 1991, 1993 # The Regents of the University of California. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the University of # California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # @(#)mkdep.gcc.sh 8.1 (Berkeley) 6/6/93 # D=.depend # default dependency file is .depend append=0 pflag= while : do case "$1" in # -a appends to the depend file -a) append=1 shift ;; # -f allows you to select a makefile name -f) D=$2 shift; shift ;; # the -p flag produces "program: program.c" style dependencies # so .o's don't get produced -p) pflag=p shift ;; *) break ;; esac done if [ $# = 0 ] ; then echo 'usage: mkdep [-p] [-f depend_file] [cc_flags] file ...' exit 1 fi DTMP=/tmp/mkdep$$ TMP=$DTMP/mkdep um=`umask` umask 022 if ! mkdir $DTMP ; then echo failed to create tmp dir $DTMP exit 1 fi umask $um trap 'rm -rf $DTMP ; trap 2 ; kill -2 $$' 1 2 3 13 15 if [ x$pflag = x ]; then ${CC:-cc} -M "$@" 2>/dev/null | sed -e 's; \./; ;g' > $TMP else ${CC:-cc} -M "$@" 2>/dev/null | sed -e 's;\.o :; :;' -e 's; \./; ;g' > $TMP fi if [ $? != 0 ]; then echo 'mkdep: compile failed.' rm -rf $DTMP exit 1 fi if [ $append = 1 ]; then cat $TMP >> $D if [ $? != 0 ]; then echo 'mkdep: append failed.' rm -rf $DTMP exit 1 fi cat $TMP | sed -e 's/\.o:/.lo:/g' >>$D if [ $? != 0 ]; then echo 'mkdep: append failed.' rm -rf $DTMP exit 1 fi else cat $TMP > $D if [ $? != 0 ]; then echo 'mkdep: overwrite failed.' rm -rf $DTMP exit 1 fi cat $TMP | sed -e 's/\.o:/.lo:/g' >>$D if [ $? != 0 ]; then echo 'mkdep: append failed.' rm -rf $DTMP exit 1 fi fi rm -rf $DTMP exit 0 libpri-1.4.3/testprilib.c0000644000000000000000000001501710452763001014025 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * This program tests libpri call reception using a zaptel interface. * Its state machines are setup for RECEIVING CALLS ONLY, so if you * are trying to both place and receive calls you have to a bit more. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SOLARIS #include #endif #include #include #include "libpri.h" #include "pri_q931.h" #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX #endif #define DEBUG_LEVEL PRI_DEBUG_ALL #define PRI_DEF_NODETYPE PRI_CPE #define PRI_DEF_SWITCHTYPE PRI_SWITCH_NI2 static struct pri *first, *cur; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; #define TEST_CALLS 32 static void event1(struct pri *pri, pri_event *e) { /* Network */ int x; static q931_call *calls[TEST_CALLS]; char name[256], num[256], dest[256]; switch(e->gen.e) { case PRI_EVENT_DCHAN_UP: printf("Network is up. Sending blast of calls!\n"); for (x=0;xgen.e), e->gen.e); q931_facility(pri, e->ringing.call); pri_answer(pri, e->ringing.call, e->ringing.channel, 0); break; case PRI_EVENT_HANGUP_REQ: printf("PRI 1: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_hangup(pri, e->hangup.call, e->hangup.cause); break; default: printf("PRI 1: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); } } static void event2(struct pri *pri, pri_event *e) { /* CPE */ switch(e->gen.e) { case PRI_EVENT_RING: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_proceeding(pri, e->ring.call, e->ring.channel, 0); pri_acknowledge(pri, e->ring.call, e->ring.channel, 0); break; case PRI_EVENT_ANSWER: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_hangup(pri, e->answer.call, PRI_CAUSE_NORMAL_UNSPECIFIED); break; case PRI_EVENT_HANGUP: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); pri_hangup(pri, e->hangup.call, e->hangup.cause); break; case PRI_EVENT_DCHAN_UP: default: printf("PRI 2: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); } } static void testmsg(struct pri *pri, char *s) { char *c; static int keeplast = 0; do { c = strchr(s, '\n'); if (c) { *c = '\0'; c++; } if (keeplast) printf("%s", s); else if (cur == first) printf("-1 %s", s); else printf("-2 %s", s); if (c) printf("\n"); s = c; } while(c && *c); if (!c) keeplast = 1; else keeplast = 0; } static void testerr(struct pri *pri, char *s) { char *c; static int keeplast = 0; do { c = strchr(s, '\n'); if (c) { *c = '\0'; c++; } if (keeplast) printf("%s", s); else if (cur == first) printf("=1 %s", s); else printf("=2 %s", s); if (c) printf("\n"); s = c; } while(c && *c); if (!c) keeplast = 1; else keeplast = 0; } static void *dchan(void *data) { /* Joint D-channel */ struct pri *pri = data; struct timeval *next, tv; pri_event *e; fd_set fds; int res; for(;;) { if ((next = pri_schedule_next(pri))) { gettimeofday(&tv, NULL); tv.tv_sec = next->tv_sec - tv.tv_sec; tv.tv_usec = next->tv_usec - tv.tv_usec; if (tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec -= 1; } if (tv.tv_sec < 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } FD_ZERO(&fds); FD_SET(pri_fd(pri), &fds); res = select(pri_fd(pri) + 1, &fds, NULL, NULL, next ? &tv : NULL); pthread_mutex_lock(&lock); cur = pri; if (res < 0) { perror("select"); } else if (!res) { e = pri_schedule_run(pri); } else { e = pri_check_event(pri); } if (e) { if (first == pri) { event1(pri, e); } else { event2(pri, e); } } pthread_mutex_unlock(&lock); } return NULL; } int main(int argc, char *argv[]) { int pair[2]; pthread_t tmp; struct pri *pri; pri_set_message(testmsg); pri_set_error(testerr); if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, pair)) { perror("socketpair"); exit(1); } if (!(pri = pri_new(pair[0], PRI_NETWORK, PRI_DEF_SWITCHTYPE))) { perror("pri(0)"); exit(1); } first = pri; pri_set_debug(pri, DEBUG_LEVEL); pri_facility_enable(pri); if (pthread_create(&tmp, NULL, dchan, pri)) { perror("thread(0)"); exit(1); } if (!(pri = pri_new(pair[1], PRI_CPE, PRI_DEF_SWITCHTYPE))) { perror("pri(1)"); exit(1); } pri_set_debug(pri, DEBUG_LEVEL); pri_facility_enable(pri); if (pthread_create(&tmp, NULL, dchan, pri)) { perror("thread(1)"); exit(1); } /* Wait for things to run */ sleep(5); exit(0); } libpri-1.4.3/compiler.h0000644000000000000000000000067110374426762013500 0ustar rootroot/* * Asterisk -- A telephony toolkit for Linux. * * Compiler-specific macros and other items * * Copyright (C) 2005, Digium, Inc. * * This program is free software, distributed under the terms of * the GNU General Public License */ #ifndef _ASTERISK_COMPILER_H #define _ASTERISK_COMPILER_H #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) #define __builtin_expect(exp, c) (exp) #endif #endif /* _ASTERISK_COMPILER_H */ libpri-1.4.3/pridump.c0000644000000000000000000000666310452763001013333 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * This program tests libpri call reception using a zaptel interface. * Its state machines are setup for RECEIVING CALLS ONLY, so if you * are trying to both place and receive calls you have to a bit more. */ #include #include #include #include #include #include #include #include #include #include #include "libpri.h" #include "pri_q921.h" #include "pri_q931.h" static int pri_open(char *dev) { int dfd; struct zt_params p; dfd = open(dev, O_RDWR); if (dfd < 0) { fprintf(stderr, "Failed to open dchannel '%s': %s\n", dev, strerror(errno)); return -1; } if (ioctl(dfd, ZT_GET_PARAMS, &p)) { fprintf(stderr, "Unable to get parameters on '%s': %s\n", dev, strerror(errno)); return -1; } if ((p.sigtype != ZT_SIG_HDLCRAW) && (p.sigtype != ZT_SIG_HDLCFCS)) { fprintf(stderr, "%s is in %d signalling, not FCS HDLC or RAW HDLC mode\n", dev, p.sigtype); return -1; } return dfd; } static void dump_packet(struct pri *pri, char *buf, int len, int txrx) { q921_h *h = (q921_h *)buf; q921_dump(pri, h, len, 1, txrx); if (!((h->h.data[0] & Q921_FRAMETYPE_MASK) & 0x3)) { q931_dump(pri, (q931_h *)(h->i.data), len - 4 - 2 /* FCS */, txrx); } fflush(stdout); fflush(stderr); } static int pri_bridge(int d1, int d2) { char buf[1024]; fd_set fds; int max; int e; int res; for(;;) { FD_ZERO(&fds); FD_SET(d1, &fds); FD_SET(d2, &fds); max = d1; if (max < d2) max = d2; ioctl(d1, ZT_GETEVENT, &e); ioctl(d2, ZT_GETEVENT, &e); res = select(max + 1, &fds, NULL, NULL, NULL); if (res < 0) { fprintf(stderr, "Select returned %d: %s\n", res, strerror(errno)); continue; }; if (FD_ISSET(d1, &fds)) { /* Copy from d1 to d2 */ res = read(d1, buf, sizeof(buf)); dump_packet((struct pri *)NULL, buf, res, 1); res = write(d2, buf, res); } if (FD_ISSET(d2, &fds)) { /* Copy from d2 to d1 */ res = read(d2, buf, sizeof(buf)); dump_packet((struct pri *)NULL, buf, res, 0); res = write(d1, buf, res); } } } static void my_pri_message(struct pri *pri, char *stuff) { fprintf(stdout, "%s", stuff); } static void my_pri_error(struct pri *pri, char *stuff) { fprintf(stderr, "%s", stuff); } int main(int argc, char *argv[]) { int d1, d2; if (argc < 3) { fprintf(stderr, "Usage: pridump \n"); exit(1); } pri_set_message(my_pri_message); pri_set_error(my_pri_error); d1 = pri_open(argv[1]); if (d1 < 0) exit(1); d2 = pri_open(argv[2]); if (d2 < 0) exit(1); pri_bridge(d1, d2); return 0; } libpri-1.4.3/pritest.c0000644000000000000000000002243110452763001013334 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * This program tests libpri call reception using a zaptel interface. * Its state machines are setup for RECEIVING CALLS ONLY, so if you * are trying to both place and receive calls you have to a bit more. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libpri.h" #define PRI_DEF_NODETYPE PRI_CPE #define PRI_DEF_SWITCHTYPE PRI_SWITCH_NI2 #define MAX_CHAN 32 #define DCHANNEL_TIMESLOT 16 static int offset = 0; static void do_channel(ZAP *z) { /* This is the part that runs on a given channel */ zap_playf(z, "raw.ulaw", 0); } struct pri_chan { pid_t pid; int needhangup; int alreadyhungup; q931_call *call; } chans[MAX_CHAN]; static int str2node(char *node) { if (!strcasecmp(node, "cpe")) return PRI_CPE; if (!strcasecmp(node, "network")) return PRI_NETWORK; return -1; } static void chan_ended(int sig) { int status; int x; struct rusage rusage; pid_t pid; pid = wait4(-1, &status, WNOHANG, &rusage); for (x=0;x -1) { fprintf(stderr, "--!! Unknown PID %d exited\n", pid); return; } } static int str2switch(char *swtype) { if (!strcasecmp(swtype, "ni2")) return PRI_SWITCH_NI2; if (!strcasecmp(swtype, "dms100")) return PRI_SWITCH_DMS100; if (!strcasecmp(swtype, "lucent5e")) return PRI_SWITCH_LUCENT5E; if (!strcasecmp(swtype, "att4ess")) return PRI_SWITCH_ATT4ESS; if (!strcasecmp(swtype, "euroisdn")) return PRI_SWITCH_EUROISDN_E1; if (!strcasecmp(swtype, "gr303eoc")) return PRI_SWITCH_GR303_EOC; if (!strcasecmp(swtype, "gr303tmc")) return PRI_SWITCH_GR303_TMC; return -1; } static void hangup_channel(int channo) { if (chans[channo].pid) { #if 0 printf("Killing channel %d (pid = %d)\n", channo, chans[channo].pid); #endif chans[channo].alreadyhungup = 1; kill(chans[channo].pid, SIGTERM); } else if (chans[channo].needhangup) chans[channo].needhangup = 0; } static void launch_channel(int channo) { pid_t pid; ZAP *z; char ch[80]; /* Make sure hangup state is reset */ chans[channo].needhangup = 0; chans[channo].alreadyhungup = 0; pid = fork(); if (pid < 0) { fprintf(stderr, "--!! Unable to fork\n"); chans[channo].needhangup = 1; } if (pid) { printf("-- Launching process %d to handle channel %d\n", pid, channo); chans[channo].pid = pid; } else { sprintf(ch, "%d", channo + offset); z = zap_open(ch, 0); if (z) { do_channel(z); exit(0); } else { fprintf(stderr, "--!! Unable to open channel %d\n", channo); exit(1); } } } static int get_free_channel(int channo) { channo--; if((channo>MAX_CHAN)||(channo<0)) { fprintf(stderr, "Invalid Bchannel RANGE <%d", channo); return 0; }; while(chans[channo].pid) { channo--; } return channo; } /* place here criteria for completion of destination number */ static int number_incommplete(char *number) { return strlen(number) < 3; } static void start_channel(struct pri *pri, pri_event *e) { int channo = e->ring.channel; int flag = 1; pri_event_ring *ring = &e->ring; if(channo == -1) { channo = e->ring.channel = get_free_channel(MAX_CHAN); if(channo == DCHANNEL_TIMESLOT) channo = e->ring.channel = get_free_channel(MAX_CHAN); fprintf(stdout, "Any channel selected: %d\n", channo); if(!channo) { pri_release(pri, ring->call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); fprintf(stdout, "Abort call due to no avl B channels\n"); return; } flag = 0; } /* Make sure it's a valid number */ if ((channo >= MAX_CHAN) || (channo < 0)) { fprintf(stderr, "--!! Channel %d is out of range\n", channo); return; } /* Make sure nothing is there */ if (chans[channo].pid) { fprintf(stderr, "--!! Channel %d still has a call on it, ending it...\n", channo); hangup_channel(channo); /* Wait for it to die */ while(chans[channo].pid) usleep(100); } /* Record call number */ chans[channo].call = e->ring.call; /* Answer the line */ if(flag) { pri_answer(pri, chans[channo].call, channo, 1); } else { pri_need_more_info(pri, chans[channo].call, channo, 1); } /* Launch a process to handle it */ launch_channel(channo); } static void handle_pri_event(struct pri *pri, pri_event *e) { switch(e->e) { case PRI_EVENT_DCHAN_UP: printf("-- D-Channel is now up! :-)\n"); break; case PRI_EVENT_DCHAN_DOWN: printf("-- D-Channel is now down! :-(\n"); break; case PRI_EVENT_RESTART: printf("-- Restarting channel %d\n", e->restart.channel); hangup_channel(e->restart.channel); break; case PRI_EVENT_CONFIG_ERR: printf("-- Configuration error detected: %s\n", e->err.err); break; case PRI_EVENT_RING: printf("-- Ring on channel %d (from %s to %s), answering...\n", e->ring.channel, e->ring.callingnum, e->ring.callednum); start_channel(pri, e); break; case PRI_EVENT_HANGUP: printf("-- Hanging up channel %d\n", e->hangup.channel); hangup_channel(e->hangup.channel); break; case PRI_EVENT_RINGING: case PRI_EVENT_ANSWER: fprintf(stderr, "--!! What? We shouldn't be making any calls...\n"); break; case PRI_EVENT_HANGUP_ACK: /* Ignore */ break; case PRI_EVENT_INFO_RECEIVED: fprintf(stdout, "number is: %s\n", e->ring.callednum); if(!number_incommplete(e->ring.callednum)) { fprintf(stdout, "final number is: %s\n", e->ring.callednum); pri_answer(pri, e->ring.call, 0, 1); } break; default: fprintf(stderr, "--!! Unknown PRI event %d\n", e->e); } } static int run_pri(int dfd, int swtype, int node) { struct pri *pri; pri_event *e; struct timeval tv = {0,0}, *next; fd_set rfds, efds; int res,x; pri = pri_new(dfd, node, swtype); if (!pri) { fprintf(stderr, "Unable to create PRI\n"); return -1; } pri_set_debug(pri, -1); for (;;) { /* Run the D-Channel */ FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(dfd, &rfds); FD_SET(dfd, &efds); if ((next = pri_schedule_next(pri))) { gettimeofday(&tv, NULL); tv.tv_sec = next->tv_sec - tv.tv_sec; tv.tv_usec = next->tv_usec - tv.tv_usec; if (tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec -= 1; } if (tv.tv_sec < 0) { tv.tv_sec = 0; tv.tv_usec = 0; } } res = select(dfd + 1, &rfds, NULL, &efds, next ? &tv : NULL); e = NULL; if (!res) { e = pri_schedule_run(pri); } else if (res > 0) { e = pri_check_event(pri); } else if (errno == ELAST) { res = ioctl(dfd, ZT_GETEVENT, &x); printf("Got Zaptel event: %d\n", x); } else if (errno != EINTR) fprintf(stderr, "Error (%d) on select: %s\n", ELAST, strerror(errno)); if (e) { handle_pri_event(pri, e); } res = ioctl(dfd, ZT_GETEVENT, &x); if (!res && x) { fprintf(stderr, "Got event on PRI interface: %d\n", x); } /* Check for lines that need hangups */ for (x=0;x [swtypetype] [nodetype]\n"); exit(1); } dfd = open(argv[1], O_RDWR); if (dfd < 0) { fprintf(stderr, "Failed to open dchannel '%s': %s\n", argv[1], strerror(errno)); exit(1); } if (ioctl(dfd, ZT_GET_PARAMS, &p)) { fprintf(stderr, "Unable to get parameters on '%s': %s\n", argv[1], strerror(errno)); exit(1); } if ((p.sigtype != ZT_SIG_HDLCRAW) && (p.sigtype != ZT_SIG_HDLCFCS)) { fprintf(stderr, "%s is in %d signalling, not FCS HDLC or RAW HDLC mode\n", argv[1], p.sigtype); exit(1); } if (argc > 2) { swtype = str2switch(argv[2]); if (swtype < 0) { fprintf(stderr, "Valid switchtypes are: ni2, dms100, lucent5e, att4ess, and euroisdn\n"); exit(1); } } if (argc > 3) { node = str2node(argv[3]); if (node < 0) { fprintf(stderr, "Valid node types are: network and cpe\n"); exit(1); } } signal(SIGCHLD, chan_ended); if (run_pri(dfd, swtype, node)) exit(1); exit(0); return 0; } libpri-1.4.3/libpri.h0000644000000000000000000005726310701465063013147 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Linux Support Services, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _LIBPRI_H #define _LIBPRI_H /* Node types */ #define PRI_NETWORK 1 #define PRI_CPE 2 /* Debugging */ #define PRI_DEBUG_Q921_RAW (1 << 0) /* Show raw HDLC frames */ #define PRI_DEBUG_Q921_DUMP (1 << 1) /* Show each interpreted Q.921 frame */ #define PRI_DEBUG_Q921_STATE (1 << 2) /* Debug state machine changes */ #define PRI_DEBUG_CONFIG (1 << 3) /* Display error events on stdout */ #define PRI_DEBUG_Q931_DUMP (1 << 5) /* Show interpreted Q.931 frames */ #define PRI_DEBUG_Q931_STATE (1 << 6) /* Debug Q.931 state machine changes */ #define PRI_DEBUG_Q931_ANOMALY (1 << 7) /* Show unexpected events */ #define PRI_DEBUG_APDU (1 << 8) /* Debug of APDU components such as ROSE */ #define PRI_DEBUG_AOC (1 << 9) /* Debug of Advice of Charge ROSE Messages */ #define PRI_DEBUG_ALL (0xffff) /* Everything */ /* Switch types */ #define PRI_SWITCH_UNKNOWN 0 #define PRI_SWITCH_NI2 1 /* National ISDN 2 */ #define PRI_SWITCH_DMS100 2 /* DMS 100 */ #define PRI_SWITCH_LUCENT5E 3 /* Lucent 5E */ #define PRI_SWITCH_ATT4ESS 4 /* AT&T 4ESS */ #define PRI_SWITCH_EUROISDN_E1 5 /* Standard EuroISDN (CTR4, ETSI 300-102) */ #define PRI_SWITCH_EUROISDN_T1 6 /* T1 EuroISDN variant (ETSI 300-102) */ #define PRI_SWITCH_NI1 7 /* National ISDN 1 */ #define PRI_SWITCH_GR303_EOC 8 /* GR-303 Embedded Operations Channel */ #define PRI_SWITCH_GR303_TMC 9 /* GR-303 Timeslot Management Channel */ #define PRI_SWITCH_QSIG 10 /* QSIG Switch */ /* Switchtypes 11 - 20 are reserved for internal use */ /* PRI D-Channel Events */ #define PRI_EVENT_DCHAN_UP 1 /* D-channel is up */ #define PRI_EVENT_DCHAN_DOWN 2 /* D-channel is down */ #define PRI_EVENT_RESTART 3 /* B-channel is restarted */ #define PRI_EVENT_CONFIG_ERR 4 /* Configuration Error Detected */ #define PRI_EVENT_RING 5 /* Incoming call */ #define PRI_EVENT_HANGUP 6 /* Call got hung up */ #define PRI_EVENT_RINGING 7 /* Call is ringing (alerting) */ #define PRI_EVENT_ANSWER 8 /* Call has been answered */ #define PRI_EVENT_HANGUP_ACK 9 /* Call hangup has been acknowledged */ #define PRI_EVENT_RESTART_ACK 10 /* Restart complete on a given channel */ #define PRI_EVENT_FACNAME 11 /* Caller*ID Name received on Facility */ #define PRI_EVENT_INFO_RECEIVED 12 /* Additional info (keypad) received */ #define PRI_EVENT_PROCEEDING 13 /* When we get CALL_PROCEEDING or PROGRESS */ #define PRI_EVENT_SETUP_ACK 14 /* When we get SETUP_ACKNOWLEDGE */ #define PRI_EVENT_HANGUP_REQ 15 /* Requesting the higher layer to hangup */ #define PRI_EVENT_NOTIFY 16 /* Notification received */ #define PRI_EVENT_PROGRESS 17 /* When we get CALL_PROCEEDING or PROGRESS */ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state */ /* Simple states */ #define PRI_STATE_DOWN 0 #define PRI_STATE_UP 1 #define PRI_PROGRESS_MASK /* Progress indicator values */ #define PRI_PROG_CALL_NOT_E2E_ISDN (1 << 0) #define PRI_PROG_CALLED_NOT_ISDN (1 << 1) #define PRI_PROG_CALLER_NOT_ISDN (1 << 2) #define PRI_PROG_INBAND_AVAILABLE (1 << 3) #define PRI_PROG_DELAY_AT_INTERF (1 << 4) #define PRI_PROG_INTERWORKING_WITH_PUBLIC (1 << 5) #define PRI_PROG_INTERWORKING_NO_RELEASE (1 << 6) #define PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER (1 << 7) #define PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER (1 << 8) #define PRI_PROG_CALLER_RETURNED_TO_ISDN (1 << 9) /* Numbering plan identifier */ #define PRI_NPI_UNKNOWN 0x0 #define PRI_NPI_E163_E164 0x1 #define PRI_NPI_X121 0x3 #define PRI_NPI_F69 0x4 #define PRI_NPI_NATIONAL 0x8 #define PRI_NPI_PRIVATE 0x9 #define PRI_NPI_RESERVED 0xF /* Type of number */ #define PRI_TON_UNKNOWN 0x0 #define PRI_TON_INTERNATIONAL 0x1 #define PRI_TON_NATIONAL 0x2 #define PRI_TON_NET_SPECIFIC 0x3 #define PRI_TON_SUBSCRIBER 0x4 #define PRI_TON_ABBREVIATED 0x6 #define PRI_TON_RESERVED 0x7 /* Redirection reasons */ #define PRI_REDIR_UNKNOWN 0x0 #define PRI_REDIR_FORWARD_ON_BUSY 0x1 #define PRI_REDIR_FORWARD_ON_NO_REPLY 0x2 #define PRI_REDIR_DEFLECTION 0x3 #define PRI_REDIR_DTE_OUT_OF_ORDER 0x9 #define PRI_REDIR_FORWARDED_BY_DTE 0xA #define PRI_REDIR_UNCONDITIONAL 0xF /* Dialing plan */ #define PRI_INTERNATIONAL_ISDN 0x11 #define PRI_NATIONAL_ISDN 0x21 #define PRI_LOCAL_ISDN 0x41 #define PRI_PRIVATE 0x49 #define PRI_UNKNOWN 0x0 /* Presentation */ #define PRES_ALLOWED_USER_NUMBER_NOT_SCREENED 0x00 #define PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN 0x01 #define PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN 0x02 #define PRES_ALLOWED_NETWORK_NUMBER 0x03 #define PRES_PROHIB_USER_NUMBER_NOT_SCREENED 0x20 #define PRES_PROHIB_USER_NUMBER_PASSED_SCREEN 0x21 #define PRES_PROHIB_USER_NUMBER_FAILED_SCREEN 0x22 #define PRES_PROHIB_NETWORK_NUMBER 0x23 #define PRES_NUMBER_NOT_AVAILABLE 0x43 /* Causes for disconnection */ #define PRI_CAUSE_UNALLOCATED 1 #define PRI_CAUSE_NO_ROUTE_TRANSIT_NET 2 /* !Q.SIG */ #define PRI_CAUSE_NO_ROUTE_DESTINATION 3 #define PRI_CAUSE_CHANNEL_UNACCEPTABLE 6 #define PRI_CAUSE_CALL_AWARDED_DELIVERED 7 /* !Q.SIG */ #define PRI_CAUSE_NORMAL_CLEARING 16 #define PRI_CAUSE_USER_BUSY 17 #define PRI_CAUSE_NO_USER_RESPONSE 18 #define PRI_CAUSE_NO_ANSWER 19 #define PRI_CAUSE_CALL_REJECTED 21 #define PRI_CAUSE_NUMBER_CHANGED 22 #define PRI_CAUSE_DESTINATION_OUT_OF_ORDER 27 #define PRI_CAUSE_INVALID_NUMBER_FORMAT 28 #define PRI_CAUSE_FACILITY_REJECTED 29 /* !Q.SIG */ #define PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY 30 #define PRI_CAUSE_NORMAL_UNSPECIFIED 31 #define PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION 34 #define PRI_CAUSE_NETWORK_OUT_OF_ORDER 38 /* !Q.SIG */ #define PRI_CAUSE_NORMAL_TEMPORARY_FAILURE 41 #define PRI_CAUSE_SWITCH_CONGESTION 42 /* !Q.SIG */ #define PRI_CAUSE_ACCESS_INFO_DISCARDED 43 /* !Q.SIG */ #define PRI_CAUSE_REQUESTED_CHAN_UNAVAIL 44 #define PRI_CAUSE_PRE_EMPTED 45 /* !Q.SIG */ #define PRI_CAUSE_FACILITY_NOT_SUBSCRIBED 50 /* !Q.SIG */ #define PRI_CAUSE_OUTGOING_CALL_BARRED 52 /* !Q.SIG */ #define PRI_CAUSE_INCOMING_CALL_BARRED 54 /* !Q.SIG */ #define PRI_CAUSE_BEARERCAPABILITY_NOTAUTH 57 #define PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL 58 #define PRI_CAUSE_SERVICEOROPTION_NOTAVAIL 63 /* Q.SIG */ #define PRI_CAUSE_BEARERCAPABILITY_NOTIMPL 65 #define PRI_CAUSE_CHAN_NOT_IMPLEMENTED 66 /* !Q.SIG */ #define PRI_CAUSE_FACILITY_NOT_IMPLEMENTED 69 /* !Q.SIG */ #define PRI_CAUSE_INVALID_CALL_REFERENCE 81 #define PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST 82 /* Q.SIG */ #define PRI_CAUSE_INCOMPATIBLE_DESTINATION 88 #define PRI_CAUSE_INVALID_MSG_UNSPECIFIED 95 /* !Q.SIG */ #define PRI_CAUSE_MANDATORY_IE_MISSING 96 #define PRI_CAUSE_MESSAGE_TYPE_NONEXIST 97 #define PRI_CAUSE_WRONG_MESSAGE 98 #define PRI_CAUSE_IE_NONEXIST 99 #define PRI_CAUSE_INVALID_IE_CONTENTS 100 #define PRI_CAUSE_WRONG_CALL_STATE 101 #define PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE 102 #define PRI_CAUSE_MANDATORY_IE_LENGTH_ERROR 103 /* !Q.SIG */ #define PRI_CAUSE_PROTOCOL_ERROR 111 #define PRI_CAUSE_INTERWORKING 127 /* !Q.SIG */ /* Transmit capabilities */ #define PRI_TRANS_CAP_SPEECH 0x0 #define PRI_TRANS_CAP_DIGITAL 0x08 #define PRI_TRANS_CAP_RESTRICTED_DIGITAL 0x09 #define PRI_TRANS_CAP_3_1K_AUDIO 0x10 #define PRI_TRANS_CAP_7K_AUDIO 0x11 /* Depriciated ITU Q.931 (05/1998)*/ #define PRI_TRANS_CAP_DIGITAL_W_TONES 0x11 #define PRI_TRANS_CAP_VIDEO 0x18 #define PRI_LAYER_1_ITU_RATE_ADAPT 0x21 #define PRI_LAYER_1_ULAW 0x22 #define PRI_LAYER_1_ALAW 0x23 #define PRI_LAYER_1_G721 0x24 #define PRI_LAYER_1_G722_G725 0x25 #define PRI_LAYER_1_H223_H245 0x26 #define PRI_LAYER_1_NON_ITU_ADAPT 0x27 #define PRI_LAYER_1_V120_RATE_ADAPT 0x28 #define PRI_LAYER_1_X31_RATE_ADAPT 0x29 /* Notifications */ #define PRI_NOTIFY_USER_SUSPENDED 0x00 /* User suspended */ #define PRI_NOTIFY_USER_RESUMED 0x01 /* User resumed */ #define PRI_NOTIFY_BEARER_CHANGE 0x02 /* Bearer service change (DSS1) */ #define PRI_NOTIFY_ASN1_COMPONENT 0x03 /* ASN.1 encoded component (DSS1) */ #define PRI_NOTIFY_COMPLETION_DELAY 0x04 /* Call completion delay */ #define PRI_NOTIFY_CONF_ESTABLISHED 0x42 /* Conference established */ #define PRI_NOTIFY_CONF_DISCONNECTED 0x43 /* Conference disconnected */ #define PRI_NOTIFY_CONF_PARTY_ADDED 0x44 /* Other party added */ #define PRI_NOTIFY_CONF_ISOLATED 0x45 /* Isolated */ #define PRI_NOTIFY_CONF_REATTACHED 0x46 /* Reattached */ #define PRI_NOTIFY_CONF_OTHER_ISOLATED 0x47 /* Other party isolated */ #define PRI_NOTIFY_CONF_OTHER_REATTACHED 0x48 /* Other party reattached */ #define PRI_NOTIFY_CONF_OTHER_SPLIT 0x49 /* Other party split */ #define PRI_NOTIFY_CONF_OTHER_DISCONNECTED 0x4a /* Other party disconnected */ #define PRI_NOTIFY_CONF_FLOATING 0x4b /* Conference floating */ #define PRI_NOTIFY_WAITING_CALL 0x60 /* Call is waiting call */ #define PRI_NOTIFY_DIVERSION_ACTIVATED 0x68 /* Diversion activated (DSS1) */ #define PRI_NOTIFY_TRANSFER_ALERTING 0x69 /* Call transfer, alerting */ #define PRI_NOTIFY_TRANSFER_ACTIVE 0x6a /* Call transfer, active */ #define PRI_NOTIFY_REMOTE_HOLD 0x79 /* Remote hold */ #define PRI_NOTIFY_REMOTE_RETRIEVAL 0x7a /* Remote retrieval */ #define PRI_NOTIFY_CALL_DIVERTING 0x7b /* Call is diverting */ #define PRI_COPY_DIGITS_CALLED_NUMBER /* Network Specific Facilities (AT&T) */ #define PRI_NSF_NONE -1 #define PRI_NSF_SID_PREFERRED 0xB1 #define PRI_NSF_ANI_PREFERRED 0xB2 #define PRI_NSF_SID_ONLY 0xB3 #define PRI_NSF_ANI_ONLY 0xB4 #define PRI_NSF_CALL_ASSOC_TSC 0xB9 #define PRI_NSF_NOTIF_CATSC_CLEARING 0xBA #define PRI_NSF_OPERATOR 0xB5 #define PRI_NSF_PCCO 0xB6 #define PRI_NSF_SDN 0xE1 #define PRI_NSF_TOLL_FREE_MEGACOM 0xE2 #define PRI_NSF_MEGACOM 0xE3 #define PRI_NSF_ACCUNET 0xE6 #define PRI_NSF_LONG_DISTANCE_SERVICE 0xE7 #define PRI_NSF_INTERNATIONAL_TOLL_FREE 0xE8 #define PRI_NSF_ATT_MULTIQUEST 0xF0 #define PRI_NSF_CALL_REDIRECTION_SERVICE 0xF7 typedef struct q931_call q931_call; typedef struct pri_event_generic { /* Events with no additional information fall in this category */ int e; } pri_event_generic; typedef struct pri_event_error { int e; char err[256]; } pri_event_error; typedef struct pri_event_restart { int e; int channel; } pri_event_restart; typedef struct pri_event_ringing { int e; int channel; int cref; int progress; int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ } pri_event_ringing; typedef struct pri_event_answer { int e; int channel; int cref; int progress; int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ } pri_event_answer; typedef struct pri_event_facname { int e; char callingname[256]; char callingnum[256]; int channel; int cref; q931_call *call; } pri_event_facname; #define PRI_CALLINGPLANANI #define PRI_CALLINGPLANRDNIS typedef struct pri_event_ring { int e; int channel; /* Channel requested */ int callingpres; /* Presentation of Calling CallerID */ int callingplanani; /* Dialing plan of Calling entity ANI */ int callingplan; /* Dialing plan of Calling entity */ char callingani[256]; /* Calling ANI */ char callingnum[256]; /* Calling number */ char callingname[256]; /* Calling name (if provided) */ int calledplan; /* Dialing plan of Called number */ int ani2; /* ANI II */ char callednum[256]; /* Called number */ char redirectingnum[256]; /* Redirecting number */ char redirectingname[256]; /* Redirecting name */ int redirectingreason; /* Reason for redirect */ int callingplanrdnis; /* Dialing plan of Redirecting Number */ char useruserinfo[260]; /* User->User info */ int flexible; /* Are we flexible with our channel selection? */ int cref; /* Call Reference Number */ int ctype; /* Call type (see PRI_TRANS_CAP_* */ int layer1; /* User layer 1 */ int complete; /* Have we seen "Complete" i.e. no more number? */ q931_call *call; /* Opaque call pointer */ char callingsubaddr[256]; /* Calling parties subaddress */ int progress; int progressmask; char origcalledname[256]; char origcallednum[256]; int callingplanorigcalled; /* Dialing plan of Originally Called Number */ int origredirectingreason; } pri_event_ring; typedef struct pri_event_hangup { int e; int channel; /* Channel requested */ int cause; int cref; q931_call *call; /* Opaque call pointer */ long aoc_units; /* Advise of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ } pri_event_hangup; typedef struct pri_event_restart_ack { int e; int channel; } pri_event_restart_ack; #define PRI_PROGRESS_CAUSE typedef struct pri_event_proceeding { int e; int channel; int cref; int progress; int progressmask; int cause; q931_call *call; } pri_event_proceeding; typedef struct pri_event_setup_ack { int e; int channel; q931_call *call; } pri_event_setup_ack; typedef struct pri_event_notify { int e; int channel; int info; } pri_event_notify; typedef struct pri_event_keypad_digit { int e; int channel; q931_call *call; char digits[64]; } pri_event_keypad_digit; typedef union { int e; pri_event_generic gen; /* Generic view */ pri_event_restart restart; /* Restart view */ pri_event_error err; /* Error view */ pri_event_facname facname; /* Caller*ID Name on Facility */ pri_event_ring ring; /* Ring */ pri_event_hangup hangup; /* Hang up */ pri_event_ringing ringing; /* Ringing */ pri_event_answer answer; /* Answer */ pri_event_restart_ack restartack; /* Restart Acknowledge */ pri_event_proceeding proceeding; /* Call proceeding & Progress */ pri_event_setup_ack setup_ack; /* SETUP_ACKNOWLEDGE structure */ pri_event_notify notify; /* Notification */ pri_event_keypad_digit digit; /* Digits that come during a call */ } pri_event; struct pri; struct pri_sr; #define PRI_IO_FUNCS /* Type declaration for callbacks to read or write a HDLC frame as below */ typedef int (*pri_io_cb)(struct pri *pri, void *buf, int buflen); /* Create a D-channel on a given file descriptor. The file descriptor must be a channel operating in HDLC mode with FCS computed by the fd's driver. Also it must be NON-BLOCKING! Frames received on the fd should include FCS. Nodetype must be one of PRI_NETWORK or PRI_CPE. switchtype should be PRI_SWITCH_* */ extern struct pri *pri_new(int fd, int nodetype, int switchtype); /* Create D-channel just as above with user defined I/O callbacks and data */ extern struct pri *pri_new_cb(int fd, int nodetype, int switchtype, pri_io_cb io_read, pri_io_cb io_write, void *userdata); /* Retrieve the user data associated with the D channel */ extern void *pri_get_userdata(struct pri *pri); /* Set the user data associated with the D channel */ extern void pri_set_userdata(struct pri *pri, void *userdata); /* Set Network Specific Facility for PRI */ extern void pri_set_nsf(struct pri *pri, int nsf); /* Set debug parameters on PRI -- see above debug definitions */ extern void pri_set_debug(struct pri *pri, int debug); /* Get debug parameters on PRI -- see above debug definitions */ extern int pri_get_debug(struct pri *pri); #define PRI_FACILITY_ENABLE /* Enable transmission support of Facility IEs on the pri */ extern void pri_facility_enable(struct pri *pri); /* Run PRI on the given D-channel, taking care of any events that need to be handled. If block is set, it will block until an event occurs which needs to be handled */ extern pri_event *pri_dchannel_run(struct pri *pri, int block); /* Check for an outstanding event on the PRI */ pri_event *pri_check_event(struct pri *pri); /* Give a name to a given event ID */ extern char *pri_event2str(int id); /* Give a name to a node type */ extern char *pri_node2str(int id); /* Give a name to a switch type */ extern char *pri_switch2str(int id); /* Print an event */ extern void pri_dump_event(struct pri *pri, pri_event *e); /* Turn presentation into a string */ extern char *pri_pres2str(int pres); /* Turn numbering plan into a string */ extern char *pri_plan2str(int plan); /* Turn cause into a string */ extern char *pri_cause2str(int cause); /* Acknowledge a call and place it on the given channel. Set info to non-zero if there is in-band data available on the channel */ extern int pri_acknowledge(struct pri *pri, q931_call *call, int channel, int info); /* Send a digit in overlap mode */ extern int pri_information(struct pri *pri, q931_call *call, char digit); #define PRI_KEYPAD_FACILITY_TX /* Send a keypad facility string of digits */ extern int pri_keypad_facility(struct pri *pri, q931_call *call, char *digits); /* Answer the incomplete(call without called number) call on the given channel. Set non-isdn to non-zero if you are not connecting to ISDN equipment */ extern int pri_need_more_info(struct pri *pri, q931_call *call, int channel, int nonisdn); /* Answer the call on the given channel (ignored if you called acknowledge already). Set non-isdn to non-zero if you are not connecting to ISDN equipment */ extern int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn); /* Set CRV reference for GR-303 calls */ #undef pri_release #undef pri_disconnect /* backwards compatibility for those who don't use asterisk with libpri */ #define pri_release(a,b,c) \ pri_hangup(a,b,c) #define pri_disconnect(a,b,c) \ pri_hangup(a,b,c) /* Hangup a call */ #define PRI_HANGUP extern int pri_hangup(struct pri *pri, q931_call *call, int cause); #define PRI_DESTROYCALL extern void pri_destroycall(struct pri *pri, q931_call *call); #define PRI_RESTART extern int pri_restart(struct pri *pri); extern int pri_reset(struct pri *pri, int channel); /* Create a new call */ extern q931_call *pri_new_call(struct pri *pri); /* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */ extern int pri_get_crv(struct pri *pri, q931_call *call, int *callmode); /* Retrieve CRV reference for GR-303 calls. CRV must be >0, call mode should be 0 */ extern int pri_set_crv(struct pri *pri, q931_call *call, int crv, int callmode); /* How long until you need to poll for a new event */ extern struct timeval *pri_schedule_next(struct pri *pri); /* Run any pending schedule events */ extern pri_event *pri_schedule_run(struct pri *pri); extern int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, char *called,int calledplan, int ulayer1); extern struct pri_sr *pri_sr_new(void); extern void pri_sr_free(struct pri_sr *sr); extern int pri_sr_set_channel(struct pri_sr *sr, int channel, int exclusive, int nonisdn); extern int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1); extern int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int complete); extern int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres); extern int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); #define PRI_USER_USER_TX /* Set the user user field. Warning! don't send binary data accross this field */ extern void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); extern void pri_call_set_useruser(q931_call *sr, const char *userchars); extern int pri_setup(struct pri *pri, q931_call *call, struct pri_sr *req); /* Set a call has a call indpendent signalling connection (i.e. no bchan) */ extern int pri_sr_set_connection_call_independent(struct pri_sr *req); /* Send an MWI indication to a remote location. If activate is non zero, activates, if zero, decativates */ extern int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); /* Send an MWI deactivate request to a remote location */ extern int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); #define PRI_2BCT /* Attempt to pass the channels back to the NET side if compatable and * suscribed. Sometimes called 2 bchannel transfer (2BCT) */ int pri_channel_bridge(q931_call *call1, q931_call *call2); /* Override message and error stuff */ #define PRI_NEW_SET_API extern void pri_set_message(void (*__pri_error)(struct pri *pri, char *)); extern void pri_set_error(void (*__pri_error)(struct pri *pri, char *)); /* Set overlap mode */ #define PRI_SET_OVERLAPDIAL extern void pri_set_overlapdial(struct pri *pri,int state); #define PRI_DUMP_INFO_STR extern char *pri_dump_info_str(struct pri *pri); /* Get file descriptor */ extern int pri_fd(struct pri *pri); #define PRI_PROGRESS /* Send call proceeding */ extern int pri_progress(struct pri *pri, q931_call *c, int channel, int info); #define PRI_PROCEEDING_FULL /* Send call proceeding */ extern int pri_proceeding(struct pri *pri, q931_call *c, int channel, int info); /* Enslave a PRI to another, so they share the same call list (and maybe some timers) */ extern void pri_enslave(struct pri *master, struct pri *slave); #define PRI_GR303_SUPPORT #define PRI_ENSLAVE_SUPPORT #define PRI_SETUP_CALL #define PRI_RECEIVE_SUBADDR #define PRI_REDIRECTING_REASON #define PRI_AOC_UNITS #define PRI_ANI /* Send notification */ extern int pri_notify(struct pri *pri, q931_call *c, int channel, int info); /* Get/Set PRI Timers */ #define PRI_GETSET_TIMERS extern int pri_set_timer(struct pri *pri, int timer, int value); extern int pri_get_timer(struct pri *pri, int timer); extern int pri_timer2idx(char *timer); #define PRI_MAX_TIMERS 32 #define PRI_TIMER_N200 0 /* Maximum numer of q921 retransmissions */ #define PRI_TIMER_N201 1 /* Maximum numer of octets in an information field */ #define PRI_TIMER_N202 2 /* Maximum numer of transmissions of the TEI identity request message */ #define PRI_TIMER_K 3 /* Maximum number of outstanding I-frames */ #define PRI_TIMER_T200 4 /* time between SABME's */ #define PRI_TIMER_T201 5 /* minimum time between retransmissions of the TEI Identity check messages */ #define PRI_TIMER_T202 6 /* minimum time between transmission of TEI Identity request messages */ #define PRI_TIMER_T203 7 /* maxiumum time without exchanging packets */ #define PRI_TIMER_T300 8 #define PRI_TIMER_T301 9 /* maximum time to respond to an ALERT */ #define PRI_TIMER_T302 10 #define PRI_TIMER_T303 11 /* maximum time to wait after sending a SETUP without a response */ #define PRI_TIMER_T304 12 #define PRI_TIMER_T305 13 #define PRI_TIMER_T306 14 #define PRI_TIMER_T307 15 #define PRI_TIMER_T308 16 #define PRI_TIMER_T309 17 #define PRI_TIMER_T310 18 /* maximum time between receiving a CALLPROCEEDING and receiving a ALERT/CONNECT/DISCONNECT/PROGRESS */ #define PRI_TIMER_T313 19 #define PRI_TIMER_T314 20 #define PRI_TIMER_T316 21 /* maximum time between transmitting a RESTART and receiving a RESTART ACK */ #define PRI_TIMER_T317 22 #define PRI_TIMER_T318 23 #define PRI_TIMER_T319 24 #define PRI_TIMER_T320 25 #define PRI_TIMER_T321 26 #define PRI_TIMER_T322 27 #endif libpri-1.4.3/TODO0000644000000000000000000000023010375150272012163 0ustar rootrootGeneral: Q.921: -- Support unnumbered information frames -- Get TEI codes working for BRI interfaces Q.931: -- Implement the 11 missing Q.931 timers libpri-1.4.3/pri_facility.h0000644000000000000000000002215310374426762014343 0ustar rootroot/* This file contains all data structures and definitions associated with facility message usage and the ROSE components included within those messages. by Matthew Fredrickson Copyright (C) Digium, Inc. 2004-2005 */ #ifndef _PRI_FACILITY_H #define _PRI_FACILITY_H /* Protocol Profile field */ #define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */ #define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */ #define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */ #define Q932_PROTOCOL_GAT 0x16 #define Q932_PROTOCOL_EXTENSIONS 0x1F /* Argument values */ #define ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE 0x80 #define ROSE_NAME_PRESENTATION_RESTRICTED_NULL 0x87 #define ROSE_NAME_NOT_AVAIL 0x84 /* Component types */ #define COMP_TYPE_INTERPRETATION 0x8B #define COMP_TYPE_NETWORK_PROTOCOL_PROFILE 0x92 #define COMP_TYPE_INVOKE 0xA1 #define COMP_TYPE_RETURN_RESULT 0xA2 #define COMP_TYPE_RETURN_ERROR 0xA3 #define COMP_TYPE_REJECT 0xA4 #define COMP_TYPE_NFE 0xAA /* Operation ID values */ /* Q.952 ROSE operations (Diverting) */ #define ROSE_DIVERTING_LEG_INFORMATION1 18 #define ROSE_DIVERTING_LEG_INFORMATION2 0x15 #define ROSE_DIVERTING_LEG_INFORMATION3 19 /* Q.956 ROSE operations (Advice Of Charge) */ #define ROSE_AOC_NO_CHARGING_INFO_AVAILABLE 26 #define ROSE_AOC_CHARGING_REQUEST 30 #define ROSE_AOC_AOCS_CURRENCY 31 #define ROSE_AOC_AOCS_SPECIAL_ARR 32 #define ROSE_AOC_AOCD_CURRENCY 33 #define ROSE_AOC_AOCD_CHARGING_UNIT 34 #define ROSE_AOC_AOCE_CURRENCY 35 #define ROSE_AOC_AOCE_CHARGING_UNIT 36 #define ROSE_AOC_IDENTIFICATION_OF_CHARGE 37 /* Q.SIG operations */ #define SS_CNID_CALLINGNAME 0 #define SS_DIVERTING_LEG_INFORMATION2 21 #define SS_MWI_ACTIVATE 80 #define SS_MWI_DEACTIVATE 81 #define SS_MWI_INTERROGATE 82 /* ROSE definitions and data structures */ #define INVOKE_IDENTIFIER 0x02 #define INVOKE_LINKED_IDENTIFIER 0x80 #define INVOKE_NULL_IDENTIFIER __USE_ASN1_NULL /* ASN.1 Identifier Octet - Data types */ #define ASN1_TYPE_MASK 0x1f #define ASN1_BOOLEAN 0x01 #define ASN1_INTEGER 0x02 #define ASN1_BITSTRING 0x03 #define ASN1_OCTETSTRING 0x04 #define ASN1_NULL 0x05 #define ASN1_OBJECTIDENTIFIER 0x06 #define ASN1_OBJECTDESCRIPTOR 0x07 #define ASN1_EXTERN 0x08 #define ASN1_REAL 0x09 #define ASN1_ENUMERATED 0x0a #define ASN1_EMBEDDEDPDV 0x0b #define ASN1_UTF8STRING 0x0c #define ASN1_RELATIVEOBJECTID 0x0d /* 0x0e & 0x0f are reserved for future ASN.1 editions */ #define ASN1_SEQUENCE 0x10 #define ASN1_SET 0x11 #define ASN1_NUMERICSTRING 0x12 #define ASN1_PRINTABLESTRING 0x13 #define ASN1_TELETEXSTRING 0x14 #define ASN1_IA5STRING 0x16 #define ASN1_UTCTIME 0x17 #define ASN1_GENERALIZEDTIME 0x18 /* ASN.1 Identifier Octet - Tags */ #define ASN1_TAG_0 0x00 #define ASN1_TAG_1 0x01 #define ASN1_TAG_2 0x02 #define ASN1_TAG_3 0x03 #define ASN1_TAG_4 0x04 #define ASN1_TAG_5 0x05 #define ASN1_TAG_6 0x06 #define ASN1_TAG_7 0x07 #define ASN1_TAG_8 0x08 #define ASN1_TAG_9 0x09 /* ASN.1 Identifier Octet - Primitive/Constructor Bit */ #define ASN1_PC_MASK 0x20 #define ASN1_PRIMITIVE 0x00 #define ASN1_CONSTRUCTOR 0x20 /* ASN.1 Identifier Octet - Clan Bits */ #define ASN1_CLAN_MASK 0xc0 #define ASN1_UNIVERSAL 0x00 #define ASN1_APPLICATION 0x40 #define ASN1_CONTEXT_SPECIFIC 0x80 #define ASN1_PRIVATE 0xc0 /* ASN.1 Length masks */ #define ASN1_LEN_INDEF 0x80 #define INVOKE_OPERATION_INT __USE_ASN1_INTEGER #define INVOKE_OBJECT_ID __USE_ASN1_OBJECTIDENTIFIER /* Q.952 Divert cause */ #define Q952_DIVERT_REASON_UNKNOWN 0x00 #define Q952_DIVERT_REASON_CFU 0x01 #define Q952_DIVERT_REASON_CFB 0x02 #define Q952_DIVERT_REASON_CFNR 0x03 #define Q952_DIVERT_REASON_CD 0x04 #define Q952_DIVERT_REASON_IMMEDIATE 0x05 /* Q.SIG Divert cause. Listed in ECMA-174 */ #define QSIG_DIVERT_REASON_UNKNOWN 0x00 /* Call forward unknown reason */ #define QSIG_DIVERT_REASON_CFU 0x01 /* Call Forward Unconditional (other reason) */ #define QSIG_DIVERT_REASON_CFB 0x02 /* Call Forward Busy */ #define QSIG_DIVERT_REASON_CFNR 0x03 /* Call Forward No Reply */ /* Q.932 Type of number */ #define Q932_TON_UNKNOWN 0x00 #define Q932_TON_INTERNATIONAL 0x01 #define Q932_TON_NATIONAL 0x02 #define Q932_TON_NET_SPECIFIC 0x03 #define Q932_TON_SUBSCRIBER 0x04 #define Q932_TON_ABBREVIATED 0x06 /* RLT related Operations */ #define RLT_SERVICE_ID 0x3e #define RLT_OPERATION_IND 0x01 #define RLT_THIRD_PARTY 0x02 struct rose_component { u_int8_t type; u_int8_t len; u_int8_t data[0]; }; #define GET_COMPONENT(component, idx, ptr, length) \ if ((idx)+2 > (length)) \ break; \ (component) = (struct rose_component*)&((ptr)[idx]); \ if ((idx)+(component)->len+2 > (length)) { \ if ((component)->len != ASN1_LEN_INDEF) \ pri_message(pri, "Length (%d) of 0x%X component is too long\n", (component)->len, (component)->type); \ } /* pri_message("XX Got component %d (0x%02X), length %d\n", (component)->type, (component)->type, (component)->len); \ if ((component)->len > 0) { \ int zzz; \ pri_message("XX Data:"); \ for (zzz = 0; zzz < (component)->len; ++zzz) \ pri_message(" %02X", (component)->data[zzz]); \ pri_message("\n"); \ } */ #define NEXT_COMPONENT(component, idx) \ (idx) += (component)->len + 2 #define SUB_COMPONENT(component, idx) \ (idx) += 2 #define CHECK_COMPONENT(component, comptype, message) \ if ((component)->type && ((component)->type & ASN1_TYPE_MASK) != (comptype)) { \ pri_message(pri, (message), (component)->type); \ asn1_dump(pri, (component), (component)->len+2); \ break; \ } #define ASN1_GET_INTEGER(component, variable) \ do { \ int comp_idx; \ (variable) = 0; \ for (comp_idx = 0; comp_idx < (component)->len; ++comp_idx) \ (variable) = ((variable) << 8) | (component)->data[comp_idx]; \ } while (0) #define ASN1_FIXUP_LEN(component, size) \ do { \ if ((component)->len == ASN1_LEN_INDEF) \ size += 2; \ } while (0) #define ASN1_ADD_SIMPLE(component, comptype, ptr, idx) \ do { \ (component) = (struct rose_component *)&((ptr)[(idx)]); \ (component)->type = (comptype); \ (component)->len = 0; \ (idx) += 2; \ } while (0) #define ASN1_ADD_BYTECOMP(component, comptype, ptr, idx, value) \ do { \ (component) = (struct rose_component *)&((ptr)[(idx)]); \ (component)->type = (comptype); \ (component)->len = 1; \ (component)->data[0] = (value); \ (idx) += 3; \ } while (0) #define ASN1_ADD_WORDCOMP(component, comptype, ptr, idx, value) \ do { \ int __val = (value); \ int __i = 0; \ (component) = (struct rose_component *)&((ptr)[(idx)]); \ (component)->type = (comptype); \ if ((__val >> 24)) \ (component)->data[__i++] = (__val >> 24) & 0xff; \ if ((__val >> 16)) \ (component)->data[__i++] = (__val >> 16) & 0xff; \ if ((__val >> 8)) \ (component)->data[__i++] = (__val >> 8) & 0xff; \ (component)->data[__i++] = __val & 0xff; \ (component)->len = __i; \ (idx) += 2 + __i; \ } while (0) #define ASN1_PUSH(stack, stackpointer, component) \ (stack)[(stackpointer)++] = (component) #define ASN1_FIXUP(stack, stackpointer, data, idx) \ do { \ --(stackpointer); \ (stack)[(stackpointer)]->len = (unsigned char *)&((data)[(idx)]) - (unsigned char *)(stack)[(stackpointer)] - 2; \ } while (0) /* Decoder for the invoke ROSE component */ int rose_invoke_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); /* Decoder for the return result ROSE component */ int rose_return_result_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); /* Decoder for the return error ROSE component */ int rose_return_error_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); /* Decoder for the reject ROSE component */ int rose_reject_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); int asn1_copy_string(char * buf, int buflen, struct rose_component *comp); int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len); /* Get Name types from ASN.1 */ int asn1_name_decode(void * data, int len, char *namebuf, int buflen); int typeofnumber_from_q931(struct pri *pri, int ton); int redirectingreason_from_q931(struct pri *pri, int redirectingreason); /* Queues an MWI apdu on a the given call */ int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req, int activate); /* starts a 2BCT */ int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); /* Use this function to queue a facility-IE born APDU onto a call * call is the call to use, messagetype is any one of the Q931 messages, * apdu is the apdu data, apdu_len is the length of the apdu data */ int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data); /* Used by q931.c to cleanup the apdu queue upon destruction of a call */ int pri_call_apdu_queue_cleanup(q931_call *call); /* Adds the "standard" APDUs to a call */ int pri_call_add_standard_apdus(struct pri *pri, q931_call *call); int asn1_dump(struct pri *pri, void *comp, int len); #endif /* _PRI_FACILITY_H */ libpri-1.4.3/pri_internal.h0000644000000000000000000001706010707136516014347 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001, Linux Support Services, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _PRI_INTERNAL_H #define _PRI_INTERNAL_H #include #include #define DBGHEAD __FILE__ ":%d %s: " #define DBGINFO __LINE__,__PRETTY_FUNCTION__ struct pri_sched { struct timeval when; void (*callback)(void *data); void *data; }; struct q921_frame; enum q931_state; enum q931_mode; /* No more than 128 scheduled events */ #define MAX_SCHED 128 #define MAX_TIMERS 32 struct pri { int fd; /* File descriptor for D-Channel */ pri_io_cb read_func; /* Read data callback */ pri_io_cb write_func; /* Write data callback */ void *userdata; struct pri *subchannel; /* Sub-channel if appropriate */ struct pri *master; /* Master channel if appropriate */ struct pri_sched pri_sched[MAX_SCHED]; /* Scheduled events */ int debug; /* Debug stuff */ int state; /* State of D-channel */ int switchtype; /* Switch type */ int nsf; /* Network-Specific Facility (if any) */ int localtype; /* Local network type (unknown, network, cpe) */ int remotetype; /* Remote network type (unknown, network, cpe) */ int sapi; int tei; int protodisc; /* Q.921 State */ int q921_state; int window; /* Max window size */ int windowlen; /* Fullness of window */ int v_s; /* Next N(S) for transmission */ int v_a; /* Last acknowledged frame */ int v_r; /* Next frame expected to be received */ int v_na; /* What we've told our peer we've acknowledged */ int solicitfbit; /* Have we sent an I or S frame with the F-bit set? */ int retrans; /* Retransmissions */ int sentrej; /* Are we in reject state */ int cref; /* Next call reference value */ int busy; /* Peer is busy */ /* Various timers */ int sabme_timer; /* SABME retransmit */ int t203_timer; /* Max idle time */ int t200_timer; /* T-200 retransmission timer */ /* All ISDN Timer values */ int timers[MAX_TIMERS]; /* Used by scheduler */ struct timeval tv; int schedev; pri_event ev; /* Static event thingy */ /* Q.921 Re-transmission queue */ struct q921_frame *txqueue; /* Q.931 calls */ q931_call **callpool; q931_call *localpool; /* do we do overlap dialing */ int overlapdial; #ifdef LIBPRI_COUNTERS /* q921/q931 packet counters */ unsigned int q921_txcount; unsigned int q921_rxcount; unsigned int q931_txcount; unsigned int q931_rxcount; #endif unsigned char last_invoke; /* Last ROSE invoke ID */ unsigned char sendfacility; }; struct pri_sr { int transmode; int channel; int exclusive; int nonisdn; char *caller; int callerplan; char *callername; int callerpres; char *called; int calledplan; int userl1; int numcomplete; char *redirectingnum; int redirectingplan; int redirectingpres; int redirectingreason; int justsignalling; const char *useruserinfo; int transferable; }; /* Internal switch types */ #define PRI_SWITCH_GR303_EOC_PATH 19 #define PRI_SWITCH_GR303_TMC_SWITCHING 20 struct apdu_event { int message; /* What message to send the ADPU in */ void (*callback)(void *data); /* Callback function for when response is received */ void *data; /* Data to callback */ unsigned char apdu[255]; /* ADPU to send */ int apdu_len; /* Length of ADPU */ int sent; /* Have we been sent already? */ struct apdu_event *next; /* Linked list pointer */ }; /* q931_call datastructure */ struct q931_call { struct pri *pri; /* PRI */ int cr; /* Call Reference */ int forceinvert; /* Force inversion of call number even if 0 */ q931_call *next; /* Slotmap specified (bitmap of channels 31/24-1) (Channel Identifier IE) (-1 means not specified) */ int slotmap; /* An explicit channel (Channel Identifier IE) (-1 means not specified) */ int channelno; /* An explicit DS1 (-1 means not specified) */ int ds1no; /* Whether or not the ds1 is explicitly identified or implicit. If implicit the bchan is on the same span as the current active dchan (NFAS) */ int ds1explicit; /* Channel flags (0 means none retrieved) */ int chanflags; int alive; /* Whether or not the call is alive */ int acked; /* Whether setup has been acked or not */ int sendhangupack; /* Whether or not to send a hangup ack */ int proc; /* Whether we've sent a call proceeding / alerting */ int ri; /* Restart Indicator (Restart Indicator IE) */ /* Bearer Capability */ int transcapability; int transmoderate; int transmultiple; int userl1; int userl2; int userl3; int rateadaption; int sentchannel; int justsignalling; /* for a signalling-only connection */ int progcode; /* Progress coding */ int progloc; /* Progress Location */ int progress; /* Progress indicator */ int progressmask; /* Progress Indicator bitmask */ int notify; /* Notification */ int causecode; /* Cause Coding */ int causeloc; /* Cause Location */ int cause; /* Cause of clearing */ int peercallstate; /* Call state of peer as reported */ int ourcallstate; /* Our call state */ int sugcallstate; /* Status call state */ int callerplan; int callerplanani; int callerpres; /* Caller presentation */ char callerani[256]; /* Caller */ char callernum[256]; char callername[256]; char keypad_digits[64]; /* Buffer for digits that come in KEYPAD_FACILITY */ int ani2; /* ANI II */ int calledplan; int nonisdn; char callednum[256]; /* Called Number */ int complete; /* no more digits coming */ int newcall; /* if the received message has a new call reference value */ int retranstimer; /* Timer for retransmitting DISC */ int t308_timedout; /* Whether t308 timed out once */ int redirectingplan; int redirectingpres; int redirectingreason; char redirectingnum[256]; /* Number of redirecting party */ char redirectingname[256]; /* Name of redirecting party */ /* Filled in cases of multiple diversions */ int origcalledplan; int origcalledpres; int origredirectingreason; /* Original reason for redirect (in cases of multiple redirects) */ char origcalledname[256]; /* Original name of person being called */ char origcallednum[256]; /* Orignal number of person being called */ int useruserprotocoldisc; char useruserinfo[256]; char callingsubaddr[256]; /* Calling parties sub address */ long aoc_units; /* Advice of Charge Units */ struct apdu_event *apdus; /* APDU queue for call */ int transferable; unsigned int rlt_call_id; /* RLT call id */ }; extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); extern pri_event *pri_schedule_run(struct pri *pri); extern void pri_schedule_del(struct pri *pri, int ev); extern pri_event *pri_mkerror(struct pri *pri, char *errstr); extern void pri_message(struct pri *pri, char *fmt, ...); extern void pri_error(struct pri *pri, char *fmt, ...); void libpri_copy_string(char *dst, const char *src, size_t size); #endif libpri-1.4.3/compat.h0000644000000000000000000000024710374426762013150 0ustar rootroot#ifndef __COMPAT_H #define __COMPAT_H #ifdef SOLARIS typedef unsigned char u_int8_t; typedef unsigned short u_int16_t; typedef unsigned int u_int32_t; #endif #endif libpri-1.4.3/pri.c0000644000000000000000000005132510670014241012435 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_facility.h" #include "pri_q921.h" #include "pri_q931.h" #include "pri_timers.h" char *pri_node2str(int node) { switch(node) { case PRI_UNKNOWN: return "Unknown node type"; case PRI_NETWORK: return "Network"; case PRI_CPE: return "CPE"; default: return "Invalid value"; } } char *pri_switch2str(int sw) { switch(sw) { case PRI_SWITCH_NI2: return "National ISDN"; case PRI_SWITCH_DMS100: return "Nortel DMS100"; case PRI_SWITCH_LUCENT5E: return "Lucent 5E"; case PRI_SWITCH_ATT4ESS: return "AT&T 4ESS"; case PRI_SWITCH_NI1: return "National ISDN 1"; case PRI_SWITCH_EUROISDN_E1: return "EuroISDN"; case PRI_SWITCH_GR303_EOC: return "GR303 EOC"; case PRI_SWITCH_GR303_TMC: return "GR303 TMC"; case PRI_SWITCH_QSIG: return "Q.SIG switch"; default: return "Unknown switchtype"; } } static void pri_default_timers(struct pri *pri, int switchtype) { int defaulttimers[20][PRI_MAX_TIMERS] = PRI_TIMERS_ALL; int x; for (x = 0; xtimers[x] = defaulttimers[switchtype][x]; } } int pri_set_timer(struct pri *pri, int timer, int value) { if (timer < 0 || timer > PRI_MAX_TIMERS || value < 0) return -1; pri->timers[timer] = value; return 0; } int pri_get_timer(struct pri *pri, int timer) { if (timer < 0 || timer > PRI_MAX_TIMERS) return -1; return pri->timers[timer]; } int pri_timer2idx(char *timer) { if (!strcasecmp(timer, "N200")) return PRI_TIMER_N200; else if (!strcasecmp(timer, "N201")) return PRI_TIMER_N201; else if (!strcasecmp(timer, "N202")) return PRI_TIMER_N202; else if (!strcasecmp(timer, "K")) return PRI_TIMER_K; else if (!strcasecmp(timer, "T200")) return PRI_TIMER_T200; else if (!strcasecmp(timer, "T202")) return PRI_TIMER_T202; else if (!strcasecmp(timer, "T203")) return PRI_TIMER_T203; else if (!strcasecmp(timer, "T300")) return PRI_TIMER_T300; else if (!strcasecmp(timer, "T301")) return PRI_TIMER_T301; else if (!strcasecmp(timer, "T302")) return PRI_TIMER_T302; else if (!strcasecmp(timer, "T303")) return PRI_TIMER_T303; else if (!strcasecmp(timer, "T304")) return PRI_TIMER_T304; else if (!strcasecmp(timer, "T305")) return PRI_TIMER_T305; else if (!strcasecmp(timer, "T306")) return PRI_TIMER_T306; else if (!strcasecmp(timer, "T307")) return PRI_TIMER_T307; else if (!strcasecmp(timer, "T308")) return PRI_TIMER_T308; else if (!strcasecmp(timer, "T309")) return PRI_TIMER_T309; else if (!strcasecmp(timer, "T310")) return PRI_TIMER_T310; else if (!strcasecmp(timer, "T313")) return PRI_TIMER_T313; else if (!strcasecmp(timer, "T314")) return PRI_TIMER_T314; else if (!strcasecmp(timer, "T316")) return PRI_TIMER_T316; else if (!strcasecmp(timer, "T317")) return PRI_TIMER_T317; else if (!strcasecmp(timer, "T318")) return PRI_TIMER_T318; else if (!strcasecmp(timer, "T319")) return PRI_TIMER_T319; else if (!strcasecmp(timer, "T320")) return PRI_TIMER_T320; else if (!strcasecmp(timer, "T321")) return PRI_TIMER_T321; else if (!strcasecmp(timer, "T322")) return PRI_TIMER_T322; else return -1; } static int __pri_read(struct pri *pri, void *buf, int buflen) { int res = read(pri->fd, buf, buflen); if (res < 0) { if (errno != EAGAIN) pri_error(pri, "Read on %d failed: %s\n", pri->fd, strerror(errno)); return 0; } return res; } static int __pri_write(struct pri *pri, void *buf, int buflen) { int res = write(pri->fd, buf, buflen); if (res < 0) { if (errno != EAGAIN) pri_error(pri, "Write to %d failed: %s\n", pri->fd, strerror(errno)); return 0; } return res; } static struct pri *__pri_new(int fd, int node, int switchtype, struct pri *master, pri_io_cb rd, pri_io_cb wr, void *userdata) { struct pri *p; p = malloc(sizeof(struct pri)); if (p) { memset(p, 0, sizeof(struct pri)); p->fd = fd; p->read_func = rd; p->write_func = wr; p->userdata = userdata; p->localtype = node; p->switchtype = switchtype; p->cref = 1; p->sapi = Q921_SAPI_CALL_CTRL; p->tei = 0; p->nsf = PRI_NSF_NONE; p->protodisc = Q931_PROTOCOL_DISCRIMINATOR; p->master = master; p->callpool = &p->localpool; pri_default_timers(p, switchtype); #ifdef LIBPRI_COUNTERS p->q921_rxcount = 0; p->q921_txcount = 0; p->q931_rxcount = 0; p->q931_txcount = 0; #endif if (switchtype == PRI_SWITCH_GR303_EOC) { p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_EOC; p->tei = Q921_TEI_GR303_EOC_OPS; p->subchannel = __pri_new(-1, node, PRI_SWITCH_GR303_EOC_PATH, p, NULL, NULL, NULL); if (!p->subchannel) { free(p); p = NULL; } } else if (switchtype == PRI_SWITCH_GR303_TMC) { p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_TMC_CALLPROC; p->tei = Q921_TEI_GR303_TMC_CALLPROC; p->subchannel = __pri_new(-1, node, PRI_SWITCH_GR303_TMC_SWITCHING, p, NULL, NULL, NULL); if (!p->subchannel) { free(p); p = NULL; } } else if (switchtype == PRI_SWITCH_GR303_TMC_SWITCHING) { p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_TMC_SWITCHING; p->tei = Q921_TEI_GR303_TMC_SWITCHING; } else if (switchtype == PRI_SWITCH_GR303_EOC_PATH) { p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_EOC; p->tei = Q921_TEI_GR303_EOC_PATH; } /* Start Q.921 layer, Wait if we're the network */ if (p) q921_start(p, p->localtype == PRI_CPE); } return p; } void pri_call_set_useruser(q931_call *c, const char *userchars) { if (userchars) libpri_copy_string(c->useruserinfo, userchars, sizeof(c->useruserinfo)); } void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars) { sr->useruserinfo = userchars; } int pri_restart(struct pri *pri) { /* Restart Q.921 layer */ if (pri) { q921_reset(pri); q921_start(pri, pri->localtype == PRI_CPE); } return 0; } struct pri *pri_new(int fd, int nodetype, int switchtype) { return __pri_new(fd, nodetype, switchtype, NULL, __pri_read, __pri_write, NULL); } struct pri *pri_new_cb(int fd, int nodetype, int switchtype, pri_io_cb io_read, pri_io_cb io_write, void *userdata) { if (!io_read) io_read = __pri_read; if (!io_write) io_write = __pri_write; return __pri_new(fd, nodetype, switchtype, NULL, io_read, io_write, userdata); } void *pri_get_userdata(struct pri *pri) { return pri ? pri->userdata : NULL; } void pri_set_userdata(struct pri *pri, void *userdata) { if (pri) pri->userdata = userdata; } void pri_set_nsf(struct pri *pri, int nsf) { if (pri) pri->nsf = nsf; } char *pri_event2str(int id) { switch(id) { case PRI_EVENT_DCHAN_UP: return "D-Channel Up"; case PRI_EVENT_DCHAN_DOWN: return "D-channel Down"; case PRI_EVENT_RESTART: return "Restart channel"; case PRI_EVENT_RING: return "Ring"; case PRI_EVENT_HANGUP: return "Hangup"; case PRI_EVENT_RINGING: return "Ringing"; case PRI_EVENT_ANSWER: return "Answer"; case PRI_EVENT_HANGUP_ACK: return "Hangup ACK"; case PRI_EVENT_RESTART_ACK: return "Restart ACK"; case PRI_EVENT_FACNAME: return "FacName"; case PRI_EVENT_INFO_RECEIVED: return "Info Received"; case PRI_EVENT_PROCEEDING: return "Proceeding"; case PRI_EVENT_SETUP_ACK: return "Setup ACK"; case PRI_EVENT_HANGUP_REQ: return "Hangup Req"; case PRI_EVENT_NOTIFY: return "Notify"; case PRI_EVENT_PROGRESS: return "Progress"; case PRI_EVENT_CONFIG_ERR: return "Configuration Error"; default: return "Unknown Event"; } } pri_event *pri_check_event(struct pri *pri) { char buf[1024]; int res; pri_event *e; res = pri->read_func ? pri->read_func(pri, buf, sizeof(buf)) : 0; if (!res) return NULL; /* Receive the q921 packet */ e = q921_receive(pri, (q921_h *)buf, res); return e; } static int wait_pri(struct pri *pri) { struct timeval *tv, real; fd_set fds; int res; FD_ZERO(&fds); FD_SET(pri->fd, &fds); tv = pri_schedule_next(pri); if (tv) { gettimeofday(&real, NULL); real.tv_sec = tv->tv_sec - real.tv_sec; real.tv_usec = tv->tv_usec - real.tv_usec; if (real.tv_usec < 0) { real.tv_usec += 1000000; real.tv_sec -= 1; } if (real.tv_sec < 0) { real.tv_sec = 0; real.tv_usec = 0; } } res = select(pri->fd + 1, &fds, NULL, NULL, tv ? &real : tv); if (res < 0) return -1; return res; } pri_event *pri_mkerror(struct pri *pri, char *errstr) { /* Return a configuration error */ pri->ev.err.e = PRI_EVENT_CONFIG_ERR; libpri_copy_string(pri->ev.err.err, errstr, sizeof(pri->ev.err.err)); return &pri->ev; } pri_event *pri_dchannel_run(struct pri *pri, int block) { pri_event *e; int res; if (!pri) return NULL; if (block) { do { e = NULL; res = wait_pri(pri); /* Check for error / interruption */ if (res < 0) return NULL; if (!res) e = pri_schedule_run(pri); else e = pri_check_event(pri); } while(!e); } else { e = pri_check_event(pri); return e; } return e; } void pri_set_debug(struct pri *pri, int debug) { if (!pri) return; pri->debug = debug; if (pri->subchannel) pri_set_debug(pri->subchannel, debug); } int pri_get_debug(struct pri *pri) { if (!pri) return -1; if (pri->subchannel) return pri_get_debug(pri->subchannel); return pri->debug; } void pri_facility_enable(struct pri *pri) { if (!pri) return; pri->sendfacility = 1; if (pri->subchannel) pri_facility_enable(pri->subchannel); return; } int pri_acknowledge(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !call) return -1; return q931_alerting(pri, call, channel, info); } int pri_proceeding(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !call) return -1; return q931_call_proceeding(pri, call, channel, info); } int pri_progress(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !call) return -1; return q931_call_progress(pri, call, channel, info); } int pri_information(struct pri *pri, q931_call *call, char digit) { if (!pri || !call) return -1; return q931_information(pri, call, digit); } int pri_keypad_facility(struct pri *pri, q931_call *call, char *digits) { if (!pri || !call || !digits || !digits[0]) return -1; return q931_keypad_facility(pri, call, digits); } int pri_notify(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !call) return -1; return q931_notify(pri, call, channel, info); } void pri_destroycall(struct pri *pri, q931_call *call) { if (pri && call) __q931_destroycall(pri, call); return; } int pri_need_more_info(struct pri *pri, q931_call *call, int channel, int nonisdn) { if (!pri || !call) return -1; return q931_setup_ack(pri, call, channel, nonisdn); } int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn) { if (!pri || !call) return -1; return q931_connect(pri, call, channel, nonisdn); } #if 0 /* deprecated routines, use pri_hangup */ int pri_release(struct pri *pri, q931_call *call, int cause) { if (!pri || !call) return -1; return q931_release(pri, call, cause); } int pri_disconnect(struct pri *pri, q931_call *call, int cause) { if (!pri || !call) return -1; return q931_disconnect(pri, call, cause); } #endif int pri_channel_bridge(q931_call *call1, q931_call *call2) { if (!call1 || !call2) return -1; /* Make sure we have compatible switchtypes */ if (call1->pri->switchtype != call2->pri->switchtype) return -1; /* Check for bearer capability */ if (call1->transcapability != call2->transcapability) return -1; /* Check to see if we're on the same PRI */ if (call1->pri != call2->pri) return -1; switch (call1->pri->switchtype) { case PRI_SWITCH_NI2: case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_ATT4ESS: if (eect_initiate_transfer(call1->pri, call1, call2)) return -1; else return 0; break; case PRI_SWITCH_DMS100: if (rlt_initiate_transfer(call1->pri, call1, call2)) return -1; else return 0; break; default: return -1; } } int pri_hangup(struct pri *pri, q931_call *call, int cause) { if (!pri || !call) return -1; if (cause == -1) /* normal clear cause */ cause = 16; return q931_hangup(pri, call, cause); } int pri_reset(struct pri *pri, int channel) { if (!pri) return -1; return q931_restart(pri, channel); } q931_call *pri_new_call(struct pri *pri) { if (!pri) return NULL; return q931_new_call(pri); } void pri_dump_event(struct pri *pri, pri_event *e) { if (!pri || !e) return; pri_message(pri, "Event type: %s (%d)\n", pri_event2str(e->gen.e), e->gen.e); switch(e->gen.e) { case PRI_EVENT_DCHAN_UP: case PRI_EVENT_DCHAN_DOWN: break; case PRI_EVENT_CONFIG_ERR: pri_message(pri, "Error: %s", e->err.err); break; case PRI_EVENT_RESTART: pri_message(pri, "Restart on channel %d\n", e->restart.channel); case PRI_EVENT_RING: pri_message(pri, "Calling number: %s (%s, %s)\n", e->ring.callingnum, pri_plan2str(e->ring.callingplan), pri_pres2str(e->ring.callingpres)); pri_message(pri, "Called number: %s (%s)\n", e->ring.callednum, pri_plan2str(e->ring.calledplan)); pri_message(pri, "Channel: %d (%s) Reference number: %d\n", e->ring.channel, e->ring.flexible ? "Flexible" : "Not Flexible", e->ring.cref); break; case PRI_EVENT_HANGUP: pri_message(pri, "Hangup, reference number: %d, reason: %s\n", e->hangup.cref, pri_cause2str(e->hangup.cause)); break; default: pri_message(pri, "Don't know how to dump events of type %d\n", e->gen.e); } } static void pri_sr_init(struct pri_sr *req) { memset(req, 0, sizeof(struct pri_sr)); } int pri_sr_set_connection_call_independent(struct pri_sr *req) { if (!req) return -1; req->justsignalling = 1; /* have to set justsignalling for all those pesky IEs we need to setup */ return 0; } /* Don't call any other pri functions on this */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan) { struct pri_sr req; if (!pri || !c) return -1; pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); req.caller = caller; req.callerplan = callerplan; req.callername = callername; req.callerpres = callerpres; req.called = called; req.calledplan = calledplan; if (mwi_message_send(pri, c, &req, 1) < 0) { pri_message(pri, "Unable to send MWI activate message\n"); return -1; } /* Do more stuff when we figure out that the CISC stuff works */ return q931_setup(pri, c, &req); } int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan) { struct pri_sr req; if (!pri || !c) return -1; pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); req.caller = caller; req.callerplan = callerplan; req.callername = callername; req.callerpres = callerpres; req.called = called; req.calledplan = calledplan; if(mwi_message_send(pri, c, &req, 0) < 0) { pri_message(pri, "Unable to send MWI deactivate message\n"); return -1; } return q931_setup(pri, c, &req); } int pri_setup(struct pri *pri, q931_call *c, struct pri_sr *req) { if (!pri || !c) return -1; return q931_setup(pri, c, req); } int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan,int ulayer1) { struct pri_sr req; if (!pri || !c) return -1; pri_sr_init(&req); req.transmode = transmode; req.channel = channel; req.exclusive = exclusive; req.nonisdn = nonisdn; req.caller = caller; req.callerplan = callerplan; req.callername = callername; req.callerpres = callerpres; req.called = called; req.calledplan = calledplan; req.userl1 = ulayer1; return q931_setup(pri, c, &req); } static void (*__pri_error)(struct pri *pri, char *stuff); static void (*__pri_message)(struct pri *pri, char *stuff); void pri_set_message(void (*func)(struct pri *pri, char *stuff)) { __pri_message = func; } void pri_set_error(void (*func)(struct pri *pri, char *stuff)) { __pri_error = func; } void pri_message(struct pri *pri, char *fmt, ...) { char tmp[1024]; va_list ap; va_start(ap, fmt); vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (__pri_message) __pri_message(pri, tmp); else fputs(tmp, stdout); } void pri_error(struct pri *pri, char *fmt, ...) { char tmp[1024]; va_list ap; va_start(ap, fmt); vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (__pri_error) __pri_error(pri, tmp); else fputs(tmp, stderr); } /* Set overlap mode */ void pri_set_overlapdial(struct pri *pri,int state) { pri->overlapdial = state; } int pri_fd(struct pri *pri) { return pri->fd; } char *pri_dump_info_str(struct pri *pri) { char buf[4096]; int len = 0; #ifdef LIBPRI_COUNTERS struct q921_frame *f; int q921outstanding = 0; #endif if (!pri) return NULL; /* Might be nice to format these a little better */ len += sprintf(buf + len, "Switchtype: %s\n", pri_switch2str(pri->switchtype)); len += sprintf(buf + len, "Type: %s\n", pri_node2str(pri->localtype)); #ifdef LIBPRI_COUNTERS /* Remember that Q921 Counters include Q931 packets (and any retransmissions) */ len += sprintf(buf + len, "Q931 RX: %d\n", pri->q931_rxcount); len += sprintf(buf + len, "Q931 TX: %d\n", pri->q931_txcount); len += sprintf(buf + len, "Q921 RX: %d\n", pri->q921_rxcount); len += sprintf(buf + len, "Q921 TX: %d\n", pri->q921_txcount); f = pri->txqueue; while (f) { q921outstanding++; f = f->next; } len += sprintf(buf + len, "Q921 Outstanding: %d\n", q921outstanding); #endif len += sprintf(buf + len, "Window Length: %d/%d\n", pri->windowlen, pri->window); len += sprintf(buf + len, "Sentrej: %d\n", pri->sentrej); len += sprintf(buf + len, "SolicitFbit: %d\n", pri->solicitfbit); len += sprintf(buf + len, "Retrans: %d\n", pri->retrans); len += sprintf(buf + len, "Busy: %d\n", pri->busy); len += sprintf(buf + len, "Overlap Dial: %d\n", pri->overlapdial); len += sprintf(buf + len, "T200 Timer: %d\n", pri->timers[PRI_TIMER_T200]); len += sprintf(buf + len, "T203 Timer: %d\n", pri->timers[PRI_TIMER_T203]); len += sprintf(buf + len, "T305 Timer: %d\n", pri->timers[PRI_TIMER_T305]); len += sprintf(buf + len, "T308 Timer: %d\n", pri->timers[PRI_TIMER_T308]); len += sprintf(buf + len, "T309 Timer: %d\n", pri->timers[PRI_TIMER_T309]); len += sprintf(buf + len, "T313 Timer: %d\n", pri->timers[PRI_TIMER_T313]); len += sprintf(buf + len, "N200 Counter: %d\n", pri->timers[PRI_TIMER_N200]); return strdup(buf); } int pri_get_crv(struct pri *pri, q931_call *call, int *callmode) { return q931_call_getcrv(pri, call, callmode); } int pri_set_crv(struct pri *pri, q931_call *call, int crv, int callmode) { return q931_call_setcrv(pri, call, crv, callmode); } void pri_enslave(struct pri *master, struct pri *slave) { if (master && slave) slave->callpool = &master->localpool; } struct pri_sr *pri_sr_new(void) { struct pri_sr *req; req = malloc(sizeof(struct pri_sr)); if (req) pri_sr_init(req); return req; } void pri_sr_free(struct pri_sr *sr) { free(sr); } int pri_sr_set_channel(struct pri_sr *sr, int channel, int exclusive, int nonisdn) { sr->channel = channel; sr->exclusive = exclusive; sr->nonisdn = nonisdn; return 0; } int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1) { sr->transmode = transmode; sr->userl1 = userl1; return 0; } int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int numcomplete) { sr->called = called; sr->calledplan = calledplan; sr->numcomplete = numcomplete; return 0; } int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres) { sr->caller = caller; sr->callername = callername; sr->callerplan = callerplan; sr->callerpres = callerpres; return 0; } int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason) { sr->redirectingnum = num; sr->redirectingplan = plan; sr->redirectingpres = pres; sr->redirectingreason = reason; return 0; } libpri-1.4.3/Makefile0000644000000000000000000001152410664622044013145 0ustar rootroot# # libpri: An implementation of Primary Rate ISDN # # Written by Mark Spencer # # Copyright (C) 2001, Linux Support Services, Inc. # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # Uncomment if you want libpri not send PROGRESS_INDICATOR w/ALERTING #ALERTING=-DALERTING_NO_PROGRESS # Uncomment if you want libpri to count number of Q921/Q931 sent/received #LIBPRI_COUNTERS=-DLIBPRI_COUNTERS CC=gcc OSARCH=$(shell uname -s) PROC?=$(shell uname -m) STATIC_LIBRARY=libpri.a DYNAMIC_LIBRARY=libpri.so.1.0 STATIC_OBJS=copy_string.o pri.o q921.o prisched.o q931.o pri_facility.o DYNAMIC_OBJS=copy_string.lo pri.lo q921.lo prisched.lo q931.lo pri_facility.lo CFLAGS=-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes -g -fPIC $(ALERTING) $(LIBPRI_COUNTERS) INSTALL_PREFIX=$(DESTDIR) INSTALL_BASE=/usr SOFLAGS = -Wl,-hlibpri.so.1.0 LDCONFIG = /sbin/ldconfig ifneq (,$(findstring X$(OSARCH)X, XLinuxX XGNU/kFreeBSDX)) LDCONFIG_FLAGS=-n else ifeq (${OSARCH},FreeBSD) LDCONFIG_FLAGS=-m CFLAGS += -I../zaptel -I../zapata INSTALL_BASE=/usr/local endif endif ifeq (${OSARCH},SunOS) CFLAGS += -DSOLARIS -I../zaptel-solaris LDCONFIG = LDCONFIG_FLAGS = \# # Trick to comment out the period in the command below SOSLINK = ln -sf libpri.so.1.0 libpri.so.1 #INSTALL_PREFIX = /opt/asterisk # Uncomment out to install in standard Solaris location for 3rd party code endif #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only. #This works for even old (2.96) versions of gcc and provides a small boost either way. #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesnt support it. ifeq ($(PROC),sparc64) PROC=ultrasparc CFLAGS += -mtune=$(PROC) -O3 -pipe -fomit-frame-pointer -mcpu=v8 endif all: depend $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) update: @if [ -d .svn ]; then \ echo "Updating from Subversion..." ; \ svn update -q; \ else \ echo "Not under version control"; \ fi install: $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) mkdir -p $(INSTALL_PREFIX)$(INSTALL_BASE)/lib mkdir -p $(INSTALL_PREFIX)$(INSTALL_BASE)/include ifneq (${OSARCH},SunOS) install -m 644 libpri.h $(INSTALL_PREFIX)$(INSTALL_BASE)/include install -m 755 $(DYNAMIC_LIBRARY) $(INSTALL_PREFIX)$(INSTALL_BASE)/lib if [ -x /usr/sbin/sestatus ] && ( /usr/sbin/sestatus | grep "SELinux status:" | grep -q "enabled"); then /sbin/restorecon -v $(INSTALL_PREFIX)$(INSTALL_BASE)/lib/$(DYNAMIC_LIBRARY); fi ( cd $(INSTALL_PREFIX)$(INSTALL_BASE)/lib ; ln -sf libpri.so.1.0 libpri.so ; ln -sf libpri.so.1.0 libpri.so.1 ) install -m 644 $(STATIC_LIBRARY) $(INSTALL_PREFIX)$(INSTALL_BASE)/lib if test $$(id -u) = 0; then $(LDCONFIG) $(LDCONFIG_FLAGS) $(INSTALL_PREFIX)$(INSTALL_BASE)/lib; fi else install -f $(INSTALL_PREFIX)$(INSTALL_BASE)/include -m 644 libpri.h install -f $(INSTALL_PREFIX)$(INSTALL_BASE)/lib -m 755 $(DYNAMIC_LIBRARY) ( cd $(INSTALL_PREFIX)$(INSTALL_BASE)/lib ; ln -sf libpri.so.1.0 libpri.so ; $(SOSLINK) ) install -f $(INSTALL_PREFIX)$(INSTALL_BASE)/lib -m 644 $(STATIC_LIBRARY) endif uninstall: @echo "Removing Libpri" rm -f $(INSTALL_PREFIX)$(INSTALL_BASE)/lib/libpri.so.1.0 rm -f $(INSTALL_PREFIX)$(INSTALL_BASE)/lib/libpri.so.1 rm -f $(INSTALL_PREFIX)$(INSTALL_BASE)/lib/libpri.so rm -f $(INSTALL_PREFIX)$(INSTALL_BASE)/lib/libpri.a rm -f $(INSTALL_PREFIX)$(INSTALL_BASE)/include/libpri.h pritest: pritest.o $(CC) -o pritest pritest.o -L. -lpri -lzap $(CFLAGS) testprilib.o: testprilib.c $(CC) $(CFLAGS) -D_REENTRANT -D_GNU_SOURCE -o $@ -c $< testprilib: testprilib.o $(CC) -o testprilib testprilib.o -L. -lpri -lpthread $(CFLAGS) pridump: pridump.o $(CC) -o pridump pridump.o -L. -lpri $(CFLAGS) ifneq ($(wildcard .depend),) include .depend endif %.lo : %.c $(CC) $(CFLAGS) -o $@ -c $< $(STATIC_LIBRARY): $(STATIC_OBJS) ar rcs $(STATIC_LIBRARY) $(STATIC_OBJS) ranlib $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY): $(DYNAMIC_OBJS) $(CC) -shared $(SOFLAGS) -o $@ $(DYNAMIC_OBJS) $(LDCONFIG) $(LDCONFIG_FLAGS) . ln -sf libpri.so.1.0 libpri.so ln -sf libpri.so.1.0 libpri.so.1 $(SOSLINK) clean: rm -f *.o *.so *.lo *.so.1 *.so.1.0 rm -f testprilib $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) rm -f pritest pridump rm -f .depend depend: .depend .depend: CC="$(CC)" ./mkdep ${CFLAGS} `ls *.c` libpri-1.4.3/q921.c0000644000000000000000000006541210453276211012347 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_q921.h" #include "pri_q931.h" /* * Define RANDOM_DROPS To randomly drop packets in order to simulate loss for testing * retransmission functionality */ /* #define RANDOM_DROPS */ #define Q921_INIT(pri, hf) do { \ memset(&(hf),0,sizeof(hf)); \ (hf).h.sapi = (pri)->sapi; \ (hf).h.ea1 = 0; \ (hf).h.ea2 = 1; \ (hf).h.tei = (pri)->tei; \ } while(0) static void reschedule_t203(struct pri *pri); static void q921_discard_retransmissions(struct pri *pri) { struct q921_frame *f, *p; f = pri->txqueue; while(f) { p = f; f = f->next; /* Free frame */ free(p); } pri->txqueue = NULL; } static int q921_transmit(struct pri *pri, q921_h *h, int len) { int res; if (pri->master) return q921_transmit(pri->master, h, len); #ifdef RANDOM_DROPS if (!(random() % 3)) { pri_message(pri, " === Dropping Packet ===\n"); return 0; } #endif #ifdef LIBPRI_COUNTERS pri->q921_txcount++; #endif /* Just send it raw */ if (pri->debug & (PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW)) q921_dump(pri, h, len, pri->debug & PRI_DEBUG_Q921_RAW, 1); /* Write an extra two bytes for the FCS */ res = pri->write_func ? pri->write_func(pri, h, len + 2) : 0; if (res != (len + 2)) { pri_error(pri, "Short write: %d/%d (%s)\n", res, len + 2, strerror(errno)); return -1; } reschedule_t203(pri); return 0; } static void q921_send_ua(struct pri *pri, int pfbit) { q921_h h; Q921_INIT(pri, h); h.u.m3 = 3; /* M3 = 3 */ h.u.m2 = 0; /* M2 = 0 */ h.u.p_f = pfbit; /* Final bit on */ h.u.ft = Q921_FRAMETYPE_U; switch(pri->localtype) { case PRI_NETWORK: h.h.c_r = 0; break; case PRI_CPE: h.h.c_r = 1; break; default: pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype); return; } if (pri->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) pri_message(pri, "Sending Unnumbered Acknowledgement\n"); q921_transmit(pri, &h, 3); } static void q921_send_sabme_now(void *vpri); static void q921_send_sabme(void *vpri, int now) { struct pri *pri = vpri; q921_h h; pri_schedule_del(pri, pri->sabme_timer); pri->sabme_timer = 0; pri->sabme_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], q921_send_sabme_now, pri); if (!now) return; Q921_INIT(pri, h); h.u.m3 = 3; /* M3 = 3 */ h.u.m2 = 3; /* M2 = 3 */ h.u.p_f = 1; /* Poll bit set */ h.u.ft = Q921_FRAMETYPE_U; switch(pri->localtype) { case PRI_NETWORK: h.h.c_r = 1; break; case PRI_CPE: h.h.c_r = 0; break; default: pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype); return; } if (pri->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) pri_message(pri, "Sending Set Asynchronous Balanced Mode Extended\n"); q921_transmit(pri, &h, 3); if (pri->debug & PRI_DEBUG_Q921_STATE && pri->q921_state != Q921_AWAITING_ESTABLISH) pri_message(pri, DBGHEAD "q921_state now is Q921_AWAITING_ESTABLISH\n", DBGINFO); pri->q921_state = Q921_AWAITING_ESTABLISH; } static void q921_send_sabme_now(void *vpri) { q921_send_sabme(vpri, 1); } static int q921_ack_packet(struct pri *pri, int num) { struct q921_frame *f, *prev = NULL; f = pri->txqueue; while(f) { if (f->h.n_s == num) { /* Cancel each packet as necessary */ /* That's our packet */ if (prev) prev->next = f->next; else pri->txqueue = f->next; if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- ACKing packet %d, new txqueue is %d (-1 means empty)\n", f->h.n_s, pri->txqueue ? pri->txqueue->h.n_s : -1); /* Update v_a */ pri->v_a = num; free(f); /* Reset retransmission counter if we actually acked something */ pri->retrans = 0; /* Decrement window size */ pri->windowlen--; /* Search for something to send */ f = pri->txqueue; while(f) { if (!f->transmitted) { /* Send it now... */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Finally transmitting %d, since window opened up\n", f->h.n_s); f->transmitted++; pri->windowlen++; f->h.n_r = pri->v_r; q921_transmit(pri, (q921_h *)(&f->h), f->len); break; } f = f->next; } return 1; } prev = f; f = f->next; } return 0; } static void t203_expire(void *); static void t200_expire(void *); static pri_event *q921_dchannel_down(struct pri *pri); static void reschedule_t203(struct pri *pri) { if (pri->t203_timer) { pri_schedule_del(pri, pri->t203_timer); if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Restarting T203 counter\n"); /* Nothing to transmit, start the T203 counter instead */ pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T203], t203_expire, pri); } } static pri_event *q921_ack_rx(struct pri *pri, int ack) { int x; int cnt=0; pri_event *ev; /* Make sure the ACK was within our window */ for (x=pri->v_a; (x != pri->v_s) && (x != ack); Q921_INC(x)); if (x != ack) { /* ACK was outside of our window --- ignore */ pri_error(pri, "ACK received for '%d' outside of window of '%d' to '%d', restarting\n", ack, pri->v_a, pri->v_s); ev = q921_dchannel_down(pri); q921_start(pri, 1); pri->schedev = 1; return ev; } /* Cancel each packet as necessary */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- ACKing all packets from %d to (but not including) %d\n", pri->v_a, ack); for (x=pri->v_a; x != ack; Q921_INC(x)) cnt += q921_ack_packet(pri, x); if (!pri->txqueue) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Since there was nothing left, stopping T200 counter\n"); /* Something was ACK'd. Stop T200 counter */ pri_schedule_del(pri, pri->t200_timer); pri->t200_timer = 0; } if (pri->t203_timer) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Stopping T203 counter since we got an ACK\n"); pri_schedule_del(pri, pri->t203_timer); pri->t203_timer = 0; } if (pri->txqueue) { /* Something left to transmit, Start the T200 counter again if we stopped it */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Something left to transmit (%d), restarting T200 counter\n", pri->txqueue->h.n_s); if (!pri->t200_timer) pri->t200_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Nothing left, starting T203 counter\n"); /* Nothing to transmit, start the T203 counter instead */ pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T203], t203_expire, pri); } return NULL; } static void q921_reject(struct pri *pri, int pf) { q921_h h; Q921_INIT(pri, h); h.s.x0 = 0; /* Always 0 */ h.s.ss = 2; /* Reject */ h.s.ft = 1; /* Frametype (01) */ h.s.n_r = pri->v_r; /* Where to start retransmission */ h.s.p_f = pf; switch(pri->localtype) { case PRI_NETWORK: h.h.c_r = 0; break; case PRI_CPE: h.h.c_r = 1; break; default: pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype); return; } if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Sending Reject (%d)\n", pri->v_r); pri->sentrej = 1; q921_transmit(pri, &h, 4); } static void q921_rr(struct pri *pri, int pbit, int cmd) { q921_h h; Q921_INIT(pri, h); h.s.x0 = 0; /* Always 0 */ h.s.ss = 0; /* Receive Ready */ h.s.ft = 1; /* Frametype (01) */ h.s.n_r = pri->v_r; /* N/R */ h.s.p_f = pbit; /* Poll/Final set appropriately */ switch(pri->localtype) { case PRI_NETWORK: if (cmd) h.h.c_r = 1; else h.h.c_r = 0; break; case PRI_CPE: if (cmd) h.h.c_r = 0; else h.h.c_r = 1; break; default: pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype); return; } pri->v_na = pri->v_r; /* Make a note that we've already acked this */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Sending Receiver Ready (%d)\n", pri->v_r); q921_transmit(pri, &h, 4); } static void t200_expire(void *vpri) { struct pri *pri = vpri; if (pri->txqueue) { /* Retransmit first packet in the queue, setting the poll bit */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- T200 counter expired, What to do...\n"); /* Force Poll bit */ pri->txqueue->h.p_f = 1; /* Update nr */ pri->txqueue->h.n_r = pri->v_r; pri->v_na = pri->v_r; pri->solicitfbit = 1; pri->retrans++; /* Up to three retransmissions */ if (pri->retrans < pri->timers[PRI_TIMER_N200]) { /* Reschedule t200_timer */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Retransmitting %d bytes\n", pri->txqueue->len); if (pri->busy) q921_rr(pri, 1, 1); else { if (!pri->txqueue->transmitted) pri_error(pri, "!! Not good - head of queue has not been transmitted yet\n"); q921_transmit(pri, (q921_h *)&pri->txqueue->h, pri->txqueue->len); } if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Rescheduling retransmission (%d)\n", pri->retrans); pri->t200_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else { if (pri->debug & PRI_DEBUG_Q921_STATE) pri_message(pri, "-- Timeout occured, restarting PRI\n"); if (pri->debug & PRI_DEBUG_Q921_STATE && pri->q921_state != Q921_LINK_CONNECTION_RELEASED) pri_message(pri, DBGHEAD "q921_state now is Q921_LINK_CONNECTION_RELEASED\n",DBGINFO); pri->q921_state = Q921_LINK_CONNECTION_RELEASED; pri->t200_timer = 0; q921_dchannel_down(pri); q921_start(pri, 1); pri->schedev = 1; } } else if (pri->solicitfbit) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Retrying poll with f-bit\n"); pri->retrans++; if (pri->retrans < pri->timers[PRI_TIMER_N200]) { pri->solicitfbit = 1; q921_rr(pri, 1, 1); pri->t200_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else { if (pri->debug & PRI_DEBUG_Q921_STATE) pri_message(pri, "-- Timeout occured, restarting PRI\n"); if (pri->debug & PRI_DEBUG_Q921_STATE && pri->q921_state != Q921_LINK_CONNECTION_RELEASED) pri_message(pri, DBGHEAD "q921_state now is Q921_LINK_CONNECTION_RELEASED\n", DBGINFO); pri->q921_state = Q921_LINK_CONNECTION_RELEASED; pri->t200_timer = 0; q921_dchannel_down(pri); q921_start(pri, 1); pri->schedev = 1; } } else { pri_error(pri, "T200 counter expired, nothing to send...\n"); pri->t200_timer = 0; } } int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr) { q921_frame *f, *prev=NULL; for (f=pri->txqueue; f; f = f->next) prev = f; f = malloc(sizeof(q921_frame) + len + 2); if (f) { memset(f,0,sizeof(q921_frame) + len + 2); Q921_INIT(pri, f->h); switch(pri->localtype) { case PRI_NETWORK: if (cr) f->h.h.c_r = 1; else f->h.h.c_r = 0; break; case PRI_CPE: if (cr) f->h.h.c_r = 0; else f->h.h.c_r = 1; break; } f->next = NULL; f->transmitted = 0; f->len = len + 4; memcpy(f->h.data, buf, len); f->h.n_s = pri->v_s; f->h.n_r = pri->v_r; pri->v_s++; pri->v_na = pri->v_r; f->h.p_f = 0; f->h.ft = 0; if (prev) prev->next = f; else pri->txqueue = f; /* Immediately transmit unless we're in a recovery state, or the window size is too big */ if (!pri->retrans && !pri->busy) { if (pri->windowlen < pri->window) { pri->windowlen++; q921_transmit(pri, (q921_h *)(&f->h), f->len); f->transmitted++; } else { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Delaying transmission of %d, window is %d/%d long\n", f->h.n_s, pri->windowlen, pri->window); } } if (pri->t203_timer) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Stopping T_203 timer\n"); pri_schedule_del(pri, pri->t203_timer); pri->t203_timer = 0; } if (!pri->t200_timer) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Starting T_200 timer\n"); pri->t200_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "T_200 timer already going (%d)\n", pri->t200_timer); } else { pri_error(pri, "!! Out of memory for Q.921 transmit\n"); return -1; } return 0; } static void t203_expire(void *vpri) { struct pri *pri = vpri; if (pri->q921_state == Q921_LINK_CONNECTION_ESTABLISHED) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "T203 counter expired, sending RR and scheduling T203 again\n"); /* Solicit an F-bit in the other's RR */ pri->solicitfbit = 1; pri->retrans = 0; q921_rr(pri, 1, 1); /* Start timer T200 to resend our RR if we don't get it */ pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "T203 counter expired in weird state %d\n", pri->q921_state); pri->t203_timer = 0; } } static pri_event *q921_handle_iframe(struct pri *pri, q921_i *i, int len) { int res; pri_event *ev; /* Make sure this is a valid packet */ if (i->n_s == pri->v_r) { /* Increment next expected I-frame */ Q921_INC(pri->v_r); /* Handle their ACK */ pri->sentrej = 0; ev = q921_ack_rx(pri, i->n_r); if (ev) return ev; if (i->p_f) { /* If the Poll/Final bit is set, immediate send the RR */ q921_rr(pri, 1, 0); } else if (pri->busy) { q921_rr(pri, 0, 0); } /* Receive Q.931 data */ res = q931_receive(pri, (q931_h *)i->data, len - 4); /* Send an RR if one wasn't sent already */ if (pri->v_na != pri->v_r) q921_rr(pri, 0, 0); if (res == -1) { return NULL; } if (res & Q931_RES_HAVEEVENT) return &pri->ev; } else { /* If we haven't already sent a reject, send it now, otherwise we are obliged to RR */ if (!pri->sentrej) q921_reject(pri, i->p_f); else if (i->p_f) q921_rr(pri, 1, 0); } return NULL; } void q921_dump(struct pri *pri, q921_h *h, int len, int showraw, int txrx) { int x; char *type; char direction_tag; direction_tag = txrx ? '>' : '<'; if (showraw) { char *buf = malloc(len * 3 + 1); int buflen = 0; if (buf) { for (x=0;xraw[x]); pri_message(pri, "\n%c [ %s]\n", direction_tag, buf); free(buf); } } switch (h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: pri_message(pri, "\n%c Informational frame:\n", direction_tag); break; case 1: pri_message(pri, "\n%c Supervisory frame:\n", direction_tag); break; case 3: pri_message(pri, "\n%c Unnumbered frame:\n", direction_tag); break; } pri_message(pri, "%c SAPI: %02d C/R: %d EA: %d\n" "%c TEI: %03d EA: %d\n", direction_tag, h->h.sapi, h->h.c_r, h->h.ea1, direction_tag, h->h.tei, h->h.ea2); switch (h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: /* Informational frame */ pri_message(pri, "%c N(S): %03d 0: %d\n" "%c N(R): %03d P: %d\n" "%c %d bytes of data\n", direction_tag, h->i.n_s, h->i.ft, direction_tag, h->i.n_r, h->i.p_f, direction_tag, len - 4); break; case 1: /* Supervisory frame */ type = "???"; switch (h->s.ss) { case 0: type = "RR (receive ready)"; break; case 1: type = "RNR (receive not ready)"; break; case 2: type = "REJ (reject)"; break; } pri_message(pri, "%c Zero: %d S: %d 01: %d [ %s ]\n" "%c N(R): %03d P/F: %d\n" "%c %d bytes of data\n", direction_tag, h->s.x0, h->s.ss, h->s.ft, type, direction_tag, h->s.n_r, h->s.p_f, direction_tag, len - 4); break; case 3: /* Unnumbered frame */ type = "???"; if (h->u.ft == 3) { switch (h->u.m3) { case 0: if (h->u.m2 == 3) type = "DM (disconnect mode)"; else if (h->u.m2 == 0) type = "UI (unnumbered information)"; break; case 2: if (h->u.m2 == 0) type = "DISC (disconnect)"; break; case 3: if (h->u.m2 == 3) type = "SABME (set asynchronous balanced mode extended)"; else if (h->u.m2 == 0) type = "UA (unnumbered acknowledgement)"; break; case 4: if (h->u.m2 == 1) type = "FRMR (frame reject)"; break; case 5: if (h->u.m2 == 3) type = "XID (exchange identification note)"; break; } } pri_message(pri, "%c M3: %d P/F: %d M2: %d 11: %d [ %s ]\n" "%c %d bytes of data\n", direction_tag, h->u.m3, h->u.p_f, h->u.m2, h->u.ft, type, direction_tag, len - 3); break; }; } static pri_event *q921_dchannel_up(struct pri *pri) { /* Reset counters, etc */ q921_reset(pri); /* Stop any SABME retransmissions */ pri_schedule_del(pri, pri->sabme_timer); pri->sabme_timer = 0; /* Reset any rejects */ pri->sentrej = 0; /* Go into connection established state */ if (pri->debug & PRI_DEBUG_Q921_STATE && pri->q921_state != Q921_LINK_CONNECTION_ESTABLISHED) pri_message(pri, DBGHEAD "q921_state now is Q921_LINK_CONNECTION_ESTABLISHED\n", DBGINFO); pri->q921_state = Q921_LINK_CONNECTION_ESTABLISHED; /* Start the T203 timer */ pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T203], t203_expire, pri); /* Notify Layer 3 */ q931_dl_indication(pri, PRI_EVENT_DCHAN_UP); /* Report event that D-Channel is now up */ pri->ev.gen.e = PRI_EVENT_DCHAN_UP; return &pri->ev; } static pri_event *q921_dchannel_down(struct pri *pri) { /* Reset counters, reset sabme timer etc */ q921_reset(pri); /* Notify Layer 3 */ q931_dl_indication(pri, PRI_EVENT_DCHAN_DOWN); /* Report event that D-Channel is now up */ pri->ev.gen.e = PRI_EVENT_DCHAN_DOWN; return &pri->ev; } void q921_reset(struct pri *pri) { /* Having gotten a SABME we MUST reset our entire state */ pri->v_s = 0; pri->v_a = 0; pri->v_r = 0; pri->v_na = 0; pri->window = pri->timers[PRI_TIMER_K]; pri->windowlen = 0; pri_schedule_del(pri, pri->sabme_timer); pri_schedule_del(pri, pri->t203_timer); pri_schedule_del(pri, pri->t200_timer); pri->sabme_timer = 0; pri->t203_timer = 0; pri->t200_timer = 0; pri->busy = 0; pri->solicitfbit = 0; if (pri->debug & PRI_DEBUG_Q921_STATE && pri->q921_state != Q921_LINK_CONNECTION_RELEASED) pri_message(pri, DBGHEAD "q921_state now is Q921_LINK_CONNECTION_RELEASED\n", DBGINFO); pri->q921_state = Q921_LINK_CONNECTION_RELEASED; pri->retrans = 0; pri->sentrej = 0; /* Discard anything waiting to go out */ q921_discard_retransmissions(pri); } static pri_event *__q921_receive_qualified(struct pri *pri, q921_h *h, int len) { q921_frame *f; pri_event *ev; int sendnow; switch(h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: if (pri->q921_state != Q921_LINK_CONNECTION_ESTABLISHED) { pri_error(pri, "!! Got I-frame while link state %d\n", pri->q921_state); return NULL; } /* Informational frame */ if (len < 4) { pri_error(pri, "!! Received short I-frame (expected 4, got %d)\n", len); break; } return q921_handle_iframe(pri, &h->i, len); break; case 1: if (pri->q921_state != Q921_LINK_CONNECTION_ESTABLISHED) { pri_error(pri, "!! Got S-frame while link down\n"); return NULL; } if (len < 4) { pri_error(pri, "!! Received short S-frame (expected 4, got %d)\n", len); break; } switch(h->s.ss) { case 0: /* Receiver Ready */ pri->busy = 0; /* Acknowledge frames as necessary */ ev = q921_ack_rx(pri, h->s.n_r); if (ev) return ev; if (h->s.p_f) { /* If it's a p/f one then send back a RR in return with the p/f bit set */ if (pri->solicitfbit) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Got RR response to our frame\n"); } else { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Unsolicited RR with P/F bit, responding\n"); q921_rr(pri, 1, 0); } pri->solicitfbit = 0; } break; case 1: /* Receiver not ready */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Got receiver not ready\n"); if(h->s.p_f) { /* Send RR if poll bit set */ q921_rr(pri, h->s.p_f, 0); } pri->busy = 1; break; case 2: /* Just retransmit */ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Got reject requesting packet %d... Retransmitting.\n", h->s.n_r); if (h->s.p_f) { /* If it has the poll bit set, send an appropriate supervisory response */ q921_rr(pri, 1, 0); } sendnow = 0; /* Resend the proper I-frame */ for(f=pri->txqueue;f;f=f->next) { if ((sendnow || (f->h.n_s == h->s.n_r)) && f->transmitted) { /* Matches the request, or follows in our window, and has already been transmitted. */ sendnow = 1; pri_error(pri, "!! Got reject for frame %d, retransmitting frame %d now, updating n_r!\n", h->s.n_r, f->h.n_s); f->h.n_r = pri->v_r; q921_transmit(pri, (q921_h *)(&f->h), f->len); } } if (!sendnow) { if (pri->txqueue) { /* This should never happen */ if (!h->s.p_f || h->s.n_r) { pri_error(pri, "!! Got reject for frame %d, but we only have others!\n", h->s.n_r); } } else { /* Hrm, we have nothing to send, but have been REJ'd. Reset v_a, v_s, etc */ pri_error(pri, "!! Got reject for frame %d, but we have nothing -- resetting!\n", h->s.n_r); pri->v_a = h->s.n_r; pri->v_s = h->s.n_r; /* Reset t200 timer if it was somehow going */ if (pri->t200_timer) { pri_schedule_del(pri, pri->t200_timer); pri->t200_timer = 0; } /* Reset and restart t203 timer */ if (pri->t203_timer) pri_schedule_del(pri, pri->t203_timer); pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T203], t203_expire, pri); } } break; default: pri_error(pri, "!! XXX Unknown Supervisory frame ss=0x%02x,pf=%02xnr=%02x vs=%02x, va=%02x XXX\n", h->s.ss, h->s.p_f, h->s.n_r, pri->v_s, pri->v_a); } break; case 3: if (len < 3) { pri_error(pri, "!! Received short unnumbered frame\n"); break; } switch(h->u.m3) { case 0: if (h->u.m2 == 3) { if (h->u.p_f) { /* Section 5.7.1 says we should restart on receiving a DM response with the f-bit set to one, but we wait T200 first */ if (pri->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) pri_message(pri, "-- Got DM Mode from peer.\n"); /* Disconnected mode, try again after T200 */ ev = q921_dchannel_down(pri); q921_start(pri, 0); return ev; } else { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Ignoring unsolicited DM with p/f set to 0\n"); #if 0 /* Requesting that we start */ q921_start(pri, 0); #endif } break; } else if (!h->u.m2) { pri_message(pri, "XXX Unnumbered Information not implemented XXX\n"); } break; case 2: if (pri->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) pri_message(pri, "-- Got Disconnect from peer.\n"); /* Acknowledge */ q921_send_ua(pri, h->u.p_f); ev = q921_dchannel_down(pri); q921_start(pri, 0); return ev; case 3: if (h->u.m2 == 3) { /* SABME */ if (pri->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) { pri_message(pri, "-- Got SABME from %s peer.\n", h->h.c_r ? "network" : "cpe"); } if (h->h.c_r) { pri->remotetype = PRI_NETWORK; if (pri->localtype == PRI_NETWORK) { /* We can't both be networks */ return pri_mkerror(pri, "We think we're the network, but they think they're the network, too."); } } else { pri->remotetype = PRI_CPE; if (pri->localtype == PRI_CPE) { /* We can't both be CPE */ return pri_mkerror(pri, "We think we're the CPE, but they think they're the CPE too.\n"); } } /* Send Unnumbered Acknowledgement */ q921_send_ua(pri, h->u.p_f); return q921_dchannel_up(pri); } else if (h->u.m2 == 0) { /* It's a UA */ if (pri->q921_state == Q921_AWAITING_ESTABLISH) { if (pri->debug & (PRI_DEBUG_Q921_STATE | PRI_DEBUG_Q921_DUMP)) { pri_message(pri, "-- Got UA from %s peer Link up.\n", h->h.c_r ? "cpe" : "network"); } return q921_dchannel_up(pri); } else pri_error(pri, "!! Got a UA, but i'm in state %d\n", pri->q921_state); } else pri_error(pri, "!! Weird frame received (m3=3, m2 = %d)\n", h->u.m2); break; case 4: pri_error(pri, "!! Frame got rejected!\n"); break; case 5: pri_error(pri, "!! XID frames not supported\n"); break; default: pri_error(pri, "!! Don't know what to do with M3=%d u-frames\n", h->u.m3); } break; } return NULL; } static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len) { pri_event *ev; /* Discard FCS */ len -= 2; if (!pri->master && pri->debug & (PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW)) q921_dump(pri, h, len, pri->debug & PRI_DEBUG_Q921_RAW, 0); /* Check some reject conditions -- Start by rejecting improper ea's */ if (h->h.ea1 || !(h->h.ea2)) return NULL; /* Check for broadcasts - not yet handled */ if (h->h.tei == Q921_TEI_GROUP) return NULL; /* Check for SAPIs we don't yet handle */ if ((h->h.sapi != pri->sapi) || (h->h.tei != pri->tei)) { #ifdef PROCESS_SUBCHANNELS /* If it's not us, try any subchannels we have */ if (pri->subchannel) return q921_receive(pri->subchannel, h, len + 2); else #endif return NULL; } ev = __q921_receive_qualified(pri, h, len); reschedule_t203(pri); return ev; } pri_event *q921_receive(struct pri *pri, q921_h *h, int len) { pri_event *e; e = __q921_receive(pri, h, len); #ifdef LIBPRI_COUNTERS pri->q921_rxcount++; #endif return e; } void q921_start(struct pri *pri, int now) { if (pri->q921_state != Q921_LINK_CONNECTION_RELEASED) { pri_error(pri, "!! q921_start: Not in 'Link Connection Released' state\n"); return; } /* Reset our interface */ q921_reset(pri); /* Do the SABME XXX Maybe we should implement T_WAIT? XXX */ q921_send_sabme(pri, now); } libpri-1.4.3/q931.c0000644000000000000000000035607710676277644012404 0ustar rootroot/* * libpri: An implementation of Primary Rate ISDN * * Written by Mark Spencer * * Copyright (C) 2001-2005, Digium * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "compat.h" #include "libpri.h" #include "pri_internal.h" #include "pri_q921.h" #include "pri_q931.h" #include "pri_facility.h" #include #include #include #include #include #include #define MAX_MAND_IES 10 struct msgtype { int msgnum; char *name; int mandies[MAX_MAND_IES]; }; static struct msgtype msgs[] = { /* Call establishment messages */ { Q931_ALERTING, "ALERTING" }, { Q931_CALL_PROCEEDING, "CALL PROCEEDING" }, { Q931_CONNECT, "CONNECT" }, { Q931_CONNECT_ACKNOWLEDGE, "CONNECT ACKNOWLEDGE" }, { Q931_PROGRESS, "PROGRESS", { Q931_PROGRESS_INDICATOR } }, { Q931_SETUP, "SETUP", { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT } }, { Q931_SETUP_ACKNOWLEDGE, "SETUP ACKNOWLEDGE" }, /* Call disestablishment messages */ { Q931_DISCONNECT, "DISCONNECT", { Q931_CAUSE } }, { Q931_RELEASE, "RELEASE" }, { Q931_RELEASE_COMPLETE, "RELEASE COMPLETE" }, { Q931_RESTART, "RESTART", { Q931_RESTART_INDICATOR } }, { Q931_RESTART_ACKNOWLEDGE, "RESTART ACKNOWLEDGE", { Q931_RESTART_INDICATOR } }, /* Miscellaneous */ { Q931_STATUS, "STATUS", { Q931_CAUSE, Q931_CALL_STATE } }, { Q931_STATUS_ENQUIRY, "STATUS ENQUIRY" }, { Q931_USER_INFORMATION, "USER_INFORMATION" }, { Q931_SEGMENT, "SEGMENT" }, { Q931_CONGESTION_CONTROL, "CONGESTION CONTROL" }, { Q931_INFORMATION, "INFORMATION" }, { Q931_FACILITY, "FACILITY" }, { Q931_NOTIFY, "NOTIFY", { Q931_IE_NOTIFY_IND } }, /* Call Management */ { Q931_HOLD, "HOLD" }, { Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" }, { Q931_HOLD_REJECT, "HOLD REJECT" }, { Q931_RETRIEVE, "RETRIEVE" }, { Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" }, { Q931_RETRIEVE_REJECT, "RETRIEVE REJECT" }, { Q931_RESUME, "RESUME" }, { Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, { Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } }, { Q931_SUSPEND, "SUSPEND" }, { Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" }, { Q931_SUSPEND_REJECT, "SUSPEND REJECT" }, /* Maintenance */ { NATIONAL_SERVICE, "SERVICE" }, { NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE" }, }; static struct msgtype causes[] = { { PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" }, { PRI_CAUSE_NO_ROUTE_TRANSIT_NET, "No route to specified transmit network" }, { PRI_CAUSE_NO_ROUTE_DESTINATION, "No route to destination" }, { PRI_CAUSE_CHANNEL_UNACCEPTABLE, "Channel unacceptable" }, { PRI_CAUSE_CALL_AWARDED_DELIVERED, "Call awarded and being delivered in an established channel" }, { PRI_CAUSE_NORMAL_CLEARING, "Normal Clearing" }, { PRI_CAUSE_USER_BUSY, "User busy" }, { PRI_CAUSE_NO_USER_RESPONSE, "No user responding" }, { PRI_CAUSE_NO_ANSWER, "User alerting, no answer" }, { PRI_CAUSE_CALL_REJECTED, "Call Rejected" }, { PRI_CAUSE_NUMBER_CHANGED, "Number changed" }, { PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" }, { PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" }, { PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" }, { PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "Response to STATus ENQuiry" }, { PRI_CAUSE_NORMAL_UNSPECIFIED, "Normal, unspecified" }, { PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION, "Circuit/channel congestion" }, { PRI_CAUSE_NETWORK_OUT_OF_ORDER, "Network out of order" }, { PRI_CAUSE_NORMAL_TEMPORARY_FAILURE, "Temporary failure" }, { PRI_CAUSE_SWITCH_CONGESTION, "Switching equipment congestion" }, { PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" }, { PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" }, { PRI_CAUSE_PRE_EMPTED, "Pre-empted" }, { PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" }, { PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" }, { PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" }, { PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" }, { PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" }, { PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" }, { PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" }, { PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" }, { PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST, "Identified channel does not exist" }, { PRI_CAUSE_INCOMPATIBLE_DESTINATION, "Incompatible destination" }, { PRI_CAUSE_INVALID_MSG_UNSPECIFIED, "Invalid message unspecified" }, { PRI_CAUSE_MANDATORY_IE_MISSING, "Mandatory information element is missing" }, { PRI_CAUSE_MESSAGE_TYPE_NONEXIST, "Message type nonexist." }, { PRI_CAUSE_WRONG_MESSAGE, "Wrong message" }, { PRI_CAUSE_IE_NONEXIST, "Info. element nonexist or not implemented" }, { PRI_CAUSE_INVALID_IE_CONTENTS, "Invalid information element contents" }, { PRI_CAUSE_WRONG_CALL_STATE, "Message not compatible with call state" }, { PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "Recover on timer expiry" }, { PRI_CAUSE_MANDATORY_IE_LENGTH_ERROR, "Mandatory IE length error" }, { PRI_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" }, { PRI_CAUSE_INTERWORKING, "Interworking, unspecified" }, }; static struct msgtype facilities[] = { { PRI_NSF_SID_PREFERRED, "CPN (SID) preferred" }, { PRI_NSF_ANI_PREFERRED, "BN (ANI) preferred" }, { PRI_NSF_SID_ONLY, "CPN (SID) only" }, { PRI_NSF_ANI_ONLY, "BN (ANI) only" }, { PRI_NSF_CALL_ASSOC_TSC, "Call Associated TSC" }, { PRI_NSF_NOTIF_CATSC_CLEARING, "Notification of CATSC Clearing or Resource Unavailable" }, { PRI_NSF_OPERATOR, "Operator" }, { PRI_NSF_PCCO, "Pre-subscribed Common Carrier Operator (PCCO)" }, { PRI_NSF_SDN, "SDN (including GSDN)" }, { PRI_NSF_TOLL_FREE_MEGACOM, "Toll Free MEGACOM" }, { PRI_NSF_MEGACOM, "MEGACOM" }, { PRI_NSF_ACCUNET, "ACCUNET Switched Digital Service" }, { PRI_NSF_LONG_DISTANCE_SERVICE, "Long Distance Service" }, { PRI_NSF_INTERNATIONAL_TOLL_FREE, "International Toll Free Service" }, { PRI_NSF_ATT_MULTIQUEST, "AT&T MultiQuest" }, { PRI_NSF_CALL_REDIRECTION_SERVICE, "Call Redirection Service" } }; #define FLAG_PREFERRED 2 #define FLAG_EXCLUSIVE 4 #define RESET_INDICATOR_CHANNEL 0 #define RESET_INDICATOR_DS1 6 #define RESET_INDICATOR_PRI 7 #define TRANS_MODE_64_CIRCUIT 0x10 #define TRANS_MODE_2x64_CIRCUIT 0x11 #define TRANS_MODE_384_CIRCUIT 0x13 #define TRANS_MODE_1536_CIRCUIT 0x15 #define TRANS_MODE_1920_CIRCUIT 0x17 #define TRANS_MODE_MULTIRATE 0x18 #define TRANS_MODE_PACKET 0x40 #define RATE_ADAPT_56K 0x0f #define LAYER_2_LAPB 0x46 #define LAYER_3_X25 0x66 /* The 4ESS uses a different audio field */ #define PRI_TRANS_CAP_AUDIO_4ESS 0x08 /* Don't forget to update PRI_PROG_xxx at libpri.h */ #define Q931_PROG_CALL_NOT_E2E_ISDN 0x01 #define Q931_PROG_CALLED_NOT_ISDN 0x02 #define Q931_PROG_CALLER_NOT_ISDN 0x03 #define Q931_PROG_CALLER_RETURNED_TO_ISDN 0x04 #define Q931_PROG_INBAND_AVAILABLE 0x08 #define Q931_PROG_DELAY_AT_INTERF 0x0a #define Q931_PROG_INTERWORKING_WITH_PUBLIC 0x10 #define Q931_PROG_INTERWORKING_NO_RELEASE 0x11 #define Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER 0x12 #define Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER 0x13 #define CODE_CCITT 0x0 #define CODE_INTERNATIONAL 0x1 #define CODE_NATIONAL 0x2 #define CODE_NETWORK_SPECIFIC 0x3 #define LOC_USER 0x0 #define LOC_PRIV_NET_LOCAL_USER 0x1 #define LOC_PUB_NET_LOCAL_USER 0x2 #define LOC_TRANSIT_NET 0x3 #define LOC_PUB_NET_REMOTE_USER 0x4 #define LOC_PRIV_NET_REMOTE_USER 0x5 #define LOC_INTERNATIONAL_NETWORK 0x7 #define LOC_NETWORK_BEYOND_INTERWORKING 0xa static char *ie2str(int ie); static char *msg2str(int msg); #define FUNC_DUMP(name) void ((name))(int full_ie, struct pri *pri, q931_ie *ie, int len, char prefix) #define FUNC_RECV(name) int ((name))(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len) #define FUNC_SEND(name) int ((name))(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len, int order) #if 1 /* Update call state with transition trace. */ #define UPDATE_OURCALLSTATE(pri,c,newstate) do {\ if (pri->debug & (PRI_DEBUG_Q931_STATE) && c->ourcallstate != newstate) \ pri_message(pri, DBGHEAD "call %d on channel %d enters state %d (%s)\n", DBGINFO, \ c->cr, c->channelno, newstate, callstate2str(newstate)); \ c->ourcallstate = newstate; \ } while (0) #else /* Update call state with no trace. */ #define UPDATE_OURCALLSTATE(pri,c,newstate) c->ourcallstate = newstate #endif struct ie { /* Maximal count of same IEs at the message (0 - any, 1..n - limited) */ int max_count; /* IE code */ int ie; /* IE friendly name */ char *name; /* Dump an IE for debugging (preceed all lines by prefix) */ FUNC_DUMP(*dump); /* Handle IE returns 0 on success, -1 on failure */ FUNC_RECV(*receive); /* Add IE to a message, return the # of bytes added or -1 on failure */ FUNC_SEND(*transmit); }; static char *code2str(int code, struct msgtype *codes, int max) { int x; for (x=0;xalive = 0; c->sendhangupack = 0; c->forceinvert = -1; c->cr = -1; c->slotmap = -1; c->channelno = -1; c->ds1no = 0; c->ds1explicit = 0; c->chanflags = 0; c->next = NULL; c->sentchannel = 0; c->newcall = 1; c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; } static char *binary(int b, int len) { static char res[33]; int x; memset(res, 0, sizeof(res)); if (len > 32) len = 32; for (x=1;x<=len;x++) res[x-1] = b & (1 << (len - x)) ? '1' : '0'; return res; } static FUNC_RECV(receive_channel_id) { int x; int pos=0; #ifdef NO_BRI_SUPPORT if (!ie->data[0] & 0x20) { pri_error(pri, "!! Not PRI type!?\n"); return -1; } #endif #ifndef NOAUTO_CHANNEL_SELECTION_SUPPORT switch (ie->data[0] & 3) { case 0: call->justsignalling = 1; break; case 1: break; default: pri_error(pri, "!! Unexpected Channel selection %d\n", ie->data[0] & 3); return -1; } #endif if (ie->data[0] & 0x08) call->chanflags = FLAG_EXCLUSIVE; else call->chanflags = FLAG_PREFERRED; pos++; if (ie->data[0] & 0x40) { /* DS1 specified -- stop here */ call->ds1no = ie->data[1] & 0x7f; call->ds1explicit = 1; pos++; } else call->ds1explicit = 0; if (pos+2 < len) { /* More coming */ if ((ie->data[pos] & 0x0f) != 3) { pri_error(pri, "!! Unexpected Channel Type %d\n", ie->data[1] & 0x0f); return -1; } if ((ie->data[pos] & 0x60) != 0) { pri_error(pri, "!! Invalid CCITT coding %d\n", (ie->data[1] & 0x60) >> 5); return -1; } if (ie->data[pos] & 0x10) { /* Expect Slot Map */ call->slotmap = 0; pos++; for (x=0;x<3;x++) { call->slotmap <<= 8; call->slotmap |= ie->data[x + pos]; } return 0; } else { pos++; /* Only expect a particular channel */ call->channelno = ie->data[pos] & 0x7f; return 0; } } else return 0; return -1; } static FUNC_SEND(transmit_channel_id) { int pos=0; /* We are ready to transmit single IE only */ if (order > 1) return 0; if (call->justsignalling) { ie->data[pos++] = 0xac; /* Read the standards docs to figure this out ECMA-165 section 7.3 */ return pos + 2; } /* Start with standard stuff */ if (pri->switchtype == PRI_SWITCH_GR303_TMC) ie->data[pos] = 0x69; else ie->data[pos] = 0xa1; /* Add exclusive flag if necessary */ if (call->chanflags & FLAG_EXCLUSIVE) ie->data[pos] |= 0x08; else if (!(call->chanflags & FLAG_PREFERRED)) { /* Don't need this IE */ return 0; } if (((pri->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit) { /* Note that we are specifying the identifier */ ie->data[pos++] |= 0x40; /* We need to use the Channel Identifier Present thingy. Just specify it and we're done */ ie->data[pos++] = 0x80 | call->ds1no; } else pos++; if ((call->channelno > -1) || (call->slotmap != -1)) { /* We'll have the octet 8.2 and 8.3's present */ ie->data[pos++] = 0x83; if (call->channelno > -1) { /* Channel number specified */ ie->data[pos++] = 0x80 | call->channelno; return pos + 2; } /* We have to send a channel map */ if (call->slotmap != -1) { ie->data[pos-1] |= 0x10; ie->data[pos++] = (call->slotmap & 0xff0000) >> 16; ie->data[pos++] = (call->slotmap & 0xff00) >> 8; ie->data[pos++] = (call->slotmap & 0xff); return pos + 2; } } if (call->ds1no > 0) { /* We're done */ return pos + 2; } pri_error(pri, "!! No channel map, no channel, and no ds1? What am I supposed to identify?\n"); return -1; } static FUNC_DUMP(dump_channel_id) { int pos=0; int x; int res = 0; static const char* msg_chan_sel[] = { "No channel selected", "B1 channel", "B2 channel","Any channel selected" "No channel selected", "As indicated in following octets", "Reserved","Any channel selected" }; pri_message(pri, "%c Channel ID (len=%2d) [ Ext: %d IntID: %s %s Spare: %d %s Dchan: %d\n", prefix, len, (ie->data[0] & 0x80) ? 1 : 0, (ie->data[0] & 0x40) ? "Explicit" : "Implicit", (ie->data[0] & 0x20) ? "PRI" : "Other", (ie->data[0] & 0x10) ? 1 : 0, (ie->data[0] & 0x08) ? "Exclusive" : "Preferred", (ie->data[0] & 0x04) ? 1 : 0); pri_message(pri, "%c ChanSel: %s\n", prefix, msg_chan_sel[(ie->data[0] & 0x3) + ((ie->data[0]>>3) & 0x4)]); pos++; len--; if (ie->data[0] & 0x40) { /* Explicitly defined DS1 */ pri_message(pri, "%c Ext: %d DS1 Identifier: %d \n", prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f); pos++; } else { /* Implicitly defined DS1 */ } if (pos+2 < len) { /* Still more information here */ pri_message(pri, "%c Ext: %d Coding: %d %s Specified Channel Type: %d\n", prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5, (ie->data[pos] & 0x10) ? "Slot Map" : "Number", ie->data[pos] & 0x0f); if (!(ie->data[pos] & 0x10)) { /* Number specified */ pos++; pri_message(pri, "%c Ext: %d Channel: %d ]\n", prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos]) & 0x7f); } else { pos++; /* Map specified */ for (x=0;x<3;x++) { res <<= 8; res |= ie->data[pos++]; } pri_message(pri, "%c Map: %s ]\n", prefix, binary(res, 24)); } } else pri_message(pri, " ]\n"); } static char *ri2str(int ri) { static struct msgtype ris[] = { { 0, "Indicated Channel" }, { 6, "Single DS1 Facility" }, { 7, "All DS1 Facilities" }, }; return code2str(ri, ris, sizeof(ris) / sizeof(ris[0])); } static FUNC_DUMP(dump_restart_indicator) { pri_message(pri, "%c Restart Indentifier (len=%2d) [ Ext: %d Spare: %d Resetting %s (%d) ]\n", prefix, len, (ie->data[0] & 0x80) >> 7, (ie->data[0] & 0x78) >> 3, ri2str(ie->data[0] & 0x7), ie->data[0] & 0x7); } static FUNC_RECV(receive_restart_indicator) { /* Pretty simple */ call->ri = ie->data[0] & 0x7; return 0; } static FUNC_SEND(transmit_restart_indicator) { /* Pretty simple */ switch(call->ri) { case 0: case 6: case 7: ie->data[0] = 0x80 | (call->ri & 0x7); break; case 5: /* Switch compatibility */ ie->data[0] = 0xA0 | (call->ri & 0x7); break; default: pri_error(pri, "!! Invalid restart indicator value %d\n", call->ri); return-1; } return 3; } static char *redirection_reason2str(int mode) { static struct msgtype modes[] = { { PRI_REDIR_UNKNOWN, "Unknown" }, { PRI_REDIR_FORWARD_ON_BUSY, "Forwarded on busy" }, { PRI_REDIR_FORWARD_ON_NO_REPLY, "Forwarded on no reply" }, { PRI_REDIR_DEFLECTION, "Call deflected" }, { PRI_REDIR_DTE_OUT_OF_ORDER, "Called DTE out of order" }, { PRI_REDIR_FORWARDED_BY_DTE, "Forwarded by called DTE" }, { PRI_REDIR_UNCONDITIONAL, "Forwarded unconditionally" }, }; return code2str(mode, modes, sizeof(modes) / sizeof(modes[0])); } static char *cap2str(int mode) { static struct msgtype modes[] = { { PRI_TRANS_CAP_SPEECH, "Speech" }, { PRI_TRANS_CAP_DIGITAL, "Unrestricted digital information" }, { PRI_TRANS_CAP_RESTRICTED_DIGITAL, "Restricted digital information" }, { PRI_TRANS_CAP_3_1K_AUDIO, "3.1kHz audio" }, { PRI_TRANS_CAP_DIGITAL_W_TONES, "Unrestricted digital information with tones/announcements" }, { PRI_TRANS_CAP_VIDEO, "Video" }, { PRI_TRANS_CAP_AUDIO_4ESS, "3.1khz audio (4ESS)" }, }; return code2str(mode, modes, sizeof(modes) / sizeof(modes[0])); } static char *mode2str(int mode) { static struct msgtype modes[] = { { TRANS_MODE_64_CIRCUIT, "64kbps, circuit-mode" }, { TRANS_MODE_2x64_CIRCUIT, "2x64kbps, circuit-mode" }, { TRANS_MODE_384_CIRCUIT, "384kbps, circuit-mode" }, { TRANS_MODE_1536_CIRCUIT, "1536kbps, circuit-mode" }, { TRANS_MODE_1920_CIRCUIT, "1920kbps, circuit-mode" }, { TRANS_MODE_MULTIRATE, "Multirate (Nx64kbps)" }, { TRANS_MODE_PACKET, "Packet Mode" }, }; return code2str(mode, modes, sizeof(modes) / sizeof(modes[0])); } static char *l12str(int proto) { static struct msgtype protos[] = { { PRI_LAYER_1_ITU_RATE_ADAPT, "ITU Rate Adaption" }, { PRI_LAYER_1_ULAW, "u-Law" }, { PRI_LAYER_1_ALAW, "A-Law" }, { PRI_LAYER_1_G721, "G.721 ADPCM" }, { PRI_LAYER_1_G722_G725, "G.722/G.725 7kHz Audio" }, { PRI_LAYER_1_H223_H245, "H.223 and H.245" }, /* Recommendation Q.931(05/98) page 60) */ { PRI_LAYER_1_NON_ITU_ADAPT, "Non-ITU Rate Adaption" }, { PRI_LAYER_1_V120_RATE_ADAPT, "V.120 Rate Adaption" }, { PRI_LAYER_1_X31_RATE_ADAPT, "X.31 Rate Adaption" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *ra2str(int proto) { static struct msgtype protos[] = { { RATE_ADAPT_56K, "from 56kbps" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *l22str(int proto) { static struct msgtype protos[] = { { LAYER_2_LAPB, "LAPB" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static char *l32str(int proto) { static struct msgtype protos[] = { { LAYER_3_X25, "X.25" }, }; return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } static FUNC_DUMP(dump_bearer_capability) { int pos=2; pri_message(pri, "%c Bearer Capability (len=%2d) [ Ext: %d Q.931 Std: %d Info transfer capability: %s (%d)\n", prefix, len, (ie->data[0] & 0x80 ) >> 7, (ie->data[0] & 0x60) >> 5, cap2str(ie->data[0] & 0x1f), (ie->data[0] & 0x1f)); pri_message(pri, "%c Ext: %d Trans mode/rate: %s (%d)\n", prefix, (ie->data[1] & 0x80) >> 7, mode2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); if ((ie->data[1] & 0x7f) == 0x18) { pri_message(pri, "%c Ext: %d Transfer rate multiplier: %d x 64\n", prefix, (ie->data[2] & 0x80) >> 7, ie->data[2] & 0x7f); pos++; } /* Stop here if no more */ if (pos >= len) return; if ((ie->data[1] & 0x7f) != TRANS_MODE_PACKET) { /* Look for octets 5 and 5.a if present */ pri_message(pri, "%c Ext: %d User information layer 1: %s (%d)\n", prefix, (ie->data[pos] >> 7), l12str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f); if ((ie->data[pos] & 0x7f) == PRI_LAYER_1_ITU_RATE_ADAPT) pri_message(pri, "%c Ext: %d Rate adaptatation: %s (%d)\n", prefix, ie->data[pos] >> 7, ra2str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f); pos++; } else { /* Look for octets 6 and 7 but not 5 and 5.a */ pri_message(pri, "%c Ext: %d User information layer 2: %s (%d)\n", prefix, ie->data[pos] >> 7, l22str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f); pos++; pri_message(pri, "%c Ext: %d User information layer 3: %s (%d)\n", prefix, ie->data[pos] >> 7, l32str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f); pos++; } } static FUNC_RECV(receive_bearer_capability) { int pos=2; if (ie->data[0] & 0x60) { pri_error(pri, "!! non-standard Q.931 standard field\n"); return -1; } call->transcapability = ie->data[0] & 0x1f; call->transmoderate = ie->data[1] & 0x7f; if (call->transmoderate == PRI_TRANS_CAP_AUDIO_4ESS) call->transmoderate = PRI_TRANS_CAP_3_1K_AUDIO; if (call->transmoderate != TRANS_MODE_PACKET) { call->userl1 = ie->data[pos] & 0x7f; if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { call->rateadaption = ie->data[++pos] & 0x7f; } pos++; } else { /* Get 6 and 7 */ call->userl2 = ie->data[pos++] & 0x7f; call->userl3 = ie->data[pos] & 0x7f; } return 0; } static FUNC_SEND(transmit_bearer_capability) { int tc; /* We are ready to transmit single IE only */ if(order > 1) return 0; tc = call->transcapability; if (pri->subchannel) { /* Bearer capability is *hard coded* in GR-303 */ ie->data[0] = 0x88; ie->data[1] = 0x90; return 4; } if (call->justsignalling) { ie->data[0] = 0xa8; ie->data[1] = 0x80; return 4; } if (pri->switchtype == PRI_SWITCH_ATT4ESS) { /* 4ESS uses a different trans capability for 3.1khz audio */ if (tc == PRI_TRANS_CAP_3_1K_AUDIO) tc = PRI_TRANS_CAP_AUDIO_4ESS; } ie->data[0] = 0x80 | tc; ie->data[1] = call->transmoderate | 0x80; if ( (tc & PRI_TRANS_CAP_DIGITAL) && (pri->switchtype == PRI_SWITCH_EUROISDN_E1) && (call->transmoderate == TRANS_MODE_PACKET) ) { /* Apparently EuroISDN switches don't seem to like user layer 2/3 */ return 4; } if (call->transmoderate != TRANS_MODE_PACKET) { /* If you have an AT&T 4ESS, you don't send any more info */ if ((pri->switchtype != PRI_SWITCH_ATT4ESS) && (call->userl1 > -1)) { ie->data[2] = call->userl1 | 0x80; /* XXX Ext bit? XXX */ if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { ie->data[3] = call->rateadaption | 0x80; return 6; } return 5; } else return 4; } else { ie->data[2] = 0x80 | call->userl2; ie->data[3] = 0x80 | call->userl3; return 6; } } char *pri_plan2str(int plan) { static struct msgtype plans[] = { { PRI_INTERNATIONAL_ISDN, "International number in ISDN" }, { PRI_NATIONAL_ISDN, "National number in ISDN" }, { PRI_LOCAL_ISDN, "Local number in ISDN" }, { PRI_PRIVATE, "Private numbering plan" }, { PRI_UNKNOWN, "Unknown numbering plan" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } static char *npi2str(int plan) { static struct msgtype plans[] = { { PRI_NPI_UNKNOWN, "Unknown Number Plan" }, { PRI_NPI_E163_E164, "ISDN/Telephony Numbering Plan (E.164/E.163)" }, { PRI_NPI_X121, "Data Numbering Plan (X.121)" }, { PRI_NPI_F69, "Telex Numbering Plan (F.69)" }, { PRI_NPI_NATIONAL, "National Standard Numbering Plan" }, { PRI_NPI_PRIVATE, "Private Numbering Plan" }, { PRI_NPI_RESERVED, "Reserved Number Plan" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } static char *ton2str(int plan) { static struct msgtype plans[] = { { PRI_TON_UNKNOWN, "Unknown Number Type" }, { PRI_TON_INTERNATIONAL, "International Number" }, { PRI_TON_NATIONAL, "National Number" }, { PRI_TON_NET_SPECIFIC, "Network Specific Number" }, { PRI_TON_SUBSCRIBER, "Subscriber Number" }, { PRI_TON_ABBREVIATED, "Abbreviated number" }, { PRI_TON_RESERVED, "Reserved Number" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } static char *subaddrtype2str(int plan) { static struct msgtype plans[] = { { 0, "NSAP (X.213/ISO 8348 AD2)" }, { 2, "User Specified" }, }; return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } char *pri_pres2str(int pres) { static struct msgtype press[] = { { PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "Presentation permitted, user number not screened" }, { PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "Presentation permitted, user number passed network screening" }, { PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "Presentation permitted, user number failed network screening" }, { PRES_ALLOWED_NETWORK_NUMBER, "Presentation allowed of network provided number" }, { PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "Presentation prohibited, user number not screened" }, { PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "Presentation prohibited, user number passed network screening" }, { PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "Presentation prohibited, user number failed network screening" }, { PRES_PROHIB_NETWORK_NUMBER, "Presentation prohibited of network provided number" }, { PRES_NUMBER_NOT_AVAILABLE, "Number not available" }, }; return code2str(pres, press, sizeof(press) / sizeof(press[0])); } static void q931_get_number(unsigned char *num, int maxlen, unsigned char *src, int len) { if ((len < 0) || (len > maxlen - 1)) { num[0] = 0; return; } memcpy(num, src, len); num[len] = 0; } static FUNC_DUMP(dump_called_party_number) { unsigned char cnum[256]; q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); pri_message(pri, "%c Called Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d) '%s' ]\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f, cnum); } static FUNC_DUMP(dump_called_party_subaddr) { unsigned char cnum[256]; q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); pri_message(pri, "%c Called Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", prefix, len, ie->data[0] >> 7, subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, (ie->data[0] & 0x08) >> 3, cnum); } static FUNC_DUMP(dump_calling_party_number) { unsigned char cnum[256]; if (ie->data[0] & 0x80) q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); else q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4); pri_message(pri, "%c Calling Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); if (ie->data[0] & 0x80) pri_message(pri, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(0), 0, cnum); else pri_message(pri, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum); } static FUNC_DUMP(dump_calling_party_subaddr) { unsigned char cnum[256]; q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); pri_message(pri, "%c Calling Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", prefix, len, ie->data[0] >> 7, subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, (ie->data[0] & 0x08) >> 3, cnum); } static FUNC_DUMP(dump_redirecting_number) { unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch(i) { case 0: /* Octet 3 */ pri_message(pri, "%c Redirecting Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ pri_message(pri, "\n%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; case 2: /* Octet 3b */ pri_message(pri, "\n%c Ext: %d Reason: %s (%d)", prefix, ie->data[2] >> 7, redirection_reason2str(ie->data[2] & 0x7f), ie->data[2] & 0x7f); break; } } while(!(ie->data[i++]& 0x80)); q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i); pri_message(pri, " '%s' ]\n", cnum); } static FUNC_DUMP(dump_connected_number) { unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch(i) { case 0: /* Octet 3 */ pri_message(pri, "%c Connected Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ pri_message(pri, "\n%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; } } while(!(ie->data[i++]& 0x80)); q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i); pri_message(pri, " '%s' ]\n", cnum); } static FUNC_RECV(receive_redirecting_number) { int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { switch(i) { case 0: call->redirectingplan = ie->data[i] & 0x7f; break; case 1: call->redirectingpres = ie->data[i] & 0x7f; break; case 2: call->redirectingreason = ie->data[i] & 0x0f; break; } } while(!(ie->data[i++] & 0x80)); q931_get_number((unsigned char *) call->redirectingnum, sizeof(call->redirectingnum), ie->data + i, ie->len - i); return 0; } static FUNC_SEND(transmit_redirecting_number) { if (order > 1) return 0; if (call->redirectingnum && *call->redirectingnum) { ie->data[0] = call->redirectingplan; ie->data[1] = call->redirectingpres; ie->data[2] = (call->redirectingreason & 0x0f) | 0x80; memcpy(ie->data + 3, call->redirectingnum, strlen(call->redirectingnum)); return strlen(call->redirectingnum) + 3 + 2; } return 0; } static FUNC_DUMP(dump_redirecting_subaddr) { unsigned char cnum[256]; q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4); pri_message(pri, "%c Redirecting Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", prefix, len, ie->data[0] >> 7, subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, (ie->data[0] & 0x08) >> 3, cnum); } static FUNC_RECV(receive_calling_party_subaddr) { /* copy digits to call->callingsubaddr */ q931_get_number((unsigned char *) call->callingsubaddr, sizeof(call->callingsubaddr), ie->data + 1, len - 3); return 0; } static FUNC_RECV(receive_called_party_number) { /* copy digits to call->callednum */ q931_get_number((unsigned char *) call->callednum, sizeof(call->callednum), ie->data + 1, len - 3); call->calledplan = ie->data[0] & 0x7f; return 0; } static FUNC_SEND(transmit_called_party_number) { ie->data[0] = 0x80 | call->calledplan; if (*call->callednum) memcpy(ie->data + 1, call->callednum, strlen(call->callednum)); return strlen(call->callednum) + 3; } static FUNC_RECV(receive_calling_party_number) { u_int8_t *data; size_t length; if (ie->data[0] & 0x80) { data = ie->data + 1; length = len - 3; call->callerpres = 0; /* PI presentation allowed SI user-provided, not screened */ } else { data = ie->data + 2; length = len - 4; call->callerpres = ie->data[1] & 0x7f; } if (call->callerpres == PRES_ALLOWED_NETWORK_NUMBER || call->callerpres == PRES_PROHIB_NETWORK_NUMBER) { q931_get_number((u_int8_t *)call->callerani, sizeof(call->callerani), data, length); call->callerplanani = ie->data[0] & 0x7f; if (!*call->callernum) { /*Copy ANI to CallerID if CallerID is not already set */ libpri_copy_string(call->callernum, call->callerani, sizeof(call->callernum)); call->callerplan = call->callerplanani; } } else { q931_get_number((u_int8_t *)call->callernum, sizeof(call->callernum), data, length); call->callerplan = ie->data[0] & 0x7f; } return 0; } static FUNC_SEND(transmit_calling_party_number) { ie->data[0] = call->callerplan; ie->data[1] = 0x80 | call->callerpres; if (*call->callernum) memcpy(ie->data + 2, call->callernum, strlen(call->callernum)); return strlen(call->callernum) + 4; } static FUNC_DUMP(dump_user_user) { int x; pri_message(pri, "%c User-User Information (len=%2d) [", prefix, len); for (x=0;xlen;x++) pri_message(pri, " %02x", ie->data[x] & 0x7f); pri_message(pri, " ]\n"); } static FUNC_RECV(receive_user_user) { call->useruserprotocoldisc = ie->data[0] & 0xff; if (call->useruserprotocoldisc == 4) /* IA5 */ q931_get_number((unsigned char *) call->useruserinfo, sizeof(call->useruserinfo), ie->data + 1, len - 3); return 0; } static FUNC_SEND(transmit_user_user) { int datalen = strlen(call->useruserinfo); if (datalen > 0) { /* Restricted to 35 characters */ if (msgtype == Q931_USER_INFORMATION) { if (datalen > 260) datalen = 260; } else { if (datalen > 35) datalen = 35; } ie->data[0] = 4; /* IA5 characters */ memcpy(&ie->data[1], call->useruserinfo, datalen); call->useruserinfo[0] = '\0'; return datalen + 3; } return 0; } static char *prog2str(int prog) { static struct msgtype progs[] = { { Q931_PROG_CALL_NOT_E2E_ISDN, "Call is not end-to-end ISDN; further call progress information may be available inband." }, { Q931_PROG_CALLED_NOT_ISDN, "Called equipment is non-ISDN." }, { Q931_PROG_CALLER_NOT_ISDN, "Calling equipment is non-ISDN." }, { Q931_PROG_INBAND_AVAILABLE, "Inband information or appropriate pattern now available." }, { Q931_PROG_DELAY_AT_INTERF, "Delay in response at called Interface." }, { Q931_PROG_INTERWORKING_WITH_PUBLIC, "Interworking with a public network." }, { Q931_PROG_INTERWORKING_NO_RELEASE, "Interworking with a network unable to supply a release signal." }, { Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER, "Interworking with a network unable to supply a release signal before answer." }, { Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER, "Interworking with a network unable to supply a release signal after answer." }, }; return code2str(prog, progs, sizeof(progs) / sizeof(progs[0])); } static char *coding2str(int cod) { static struct msgtype cods[] = { { CODE_CCITT, "CCITT (ITU) standard" }, { CODE_INTERNATIONAL, "Non-ITU international standard" }, { CODE_NATIONAL, "National standard" }, { CODE_NETWORK_SPECIFIC, "Network specific standard" }, }; return code2str(cod, cods, sizeof(cods) / sizeof(cods[0])); } static char *loc2str(int loc) { static struct msgtype locs[] = { { LOC_USER, "User" }, { LOC_PRIV_NET_LOCAL_USER, "Private network serving the local user" }, { LOC_PUB_NET_LOCAL_USER, "Public network serving the local user" }, { LOC_TRANSIT_NET, "Transit network" }, { LOC_PUB_NET_REMOTE_USER, "Public network serving the remote user" }, { LOC_PRIV_NET_REMOTE_USER, "Private network serving the remote user" }, { LOC_INTERNATIONAL_NETWORK, "International network" }, { LOC_NETWORK_BEYOND_INTERWORKING, "Network beyond the interworking point" }, }; return code2str(loc, locs, sizeof(locs) / sizeof(locs[0])); } static FUNC_DUMP(dump_progress_indicator) { pri_message(pri, "%c Progress Indicator (len=%2d) [ Ext: %d Coding: %s (%d) 0: %d Location: %s (%d)\n", prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5, (ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf); pri_message(pri, "%c Ext: %d Progress Description: %s (%d) ]\n", prefix, ie->data[1] >> 7, prog2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); } static FUNC_RECV(receive_display) { unsigned char *data; data = ie->data; if (data[0] & 0x80) { /* Skip over character set */ data++; len--; } q931_get_number((unsigned char *) call->callername, sizeof(call->callername), data, len - 2); return 0; } static FUNC_SEND(transmit_display) { int i; if ((pri->switchtype == PRI_SWITCH_QSIG) || ((pri->switchtype == PRI_SWITCH_EUROISDN_E1) && (pri->localtype == PRI_CPE)) || !call->callername[0]) return 0; i = 0; if(pri->switchtype != PRI_SWITCH_EUROISDN_E1) { ie->data[0] = 0xb1; ++i; } memcpy(ie->data + i, call->callername, strlen(call->callername)); return 2 + i + strlen(call->callername); } static FUNC_RECV(receive_progress_indicator) { call->progloc = ie->data[0] & 0xf; call->progcode = (ie->data[0] & 0x60) >> 5; switch (call->progress = (ie->data[1] & 0x7f)) { case Q931_PROG_CALL_NOT_E2E_ISDN: call->progressmask |= PRI_PROG_CALL_NOT_E2E_ISDN; break; case Q931_PROG_CALLED_NOT_ISDN: call->progressmask |= PRI_PROG_CALLED_NOT_ISDN; break; case Q931_PROG_CALLER_NOT_ISDN: call->progressmask |= PRI_PROG_CALLER_NOT_ISDN; break; case Q931_PROG_CALLER_RETURNED_TO_ISDN: call->progressmask |= PRI_PROG_CALLER_RETURNED_TO_ISDN; break; case Q931_PROG_INBAND_AVAILABLE: call->progressmask |= PRI_PROG_INBAND_AVAILABLE; break; case Q931_PROG_DELAY_AT_INTERF: call->progressmask |= PRI_PROG_DELAY_AT_INTERF; break; case Q931_PROG_INTERWORKING_WITH_PUBLIC: call->progressmask |= PRI_PROG_INTERWORKING_WITH_PUBLIC; break; case Q931_PROG_INTERWORKING_NO_RELEASE: call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE; break; case Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER: call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER; break; case Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER: call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER; break; default: pri_error(pri, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f)); break; } return 0; } static FUNC_SEND(transmit_facility) { struct apdu_event *tmp; int i = 0; for (tmp = call->apdus; tmp; tmp = tmp->next) { if ((tmp->message == msgtype) && !tmp->sent) break; } if (!tmp) /* No APDU found */ return 0; if (tmp->apdu_len > 235) { /* TODO: find out how much space we can use */ pri_message(pri, "Requested APDU (%d bytes) is too long\n", tmp->apdu_len); return 0; } memcpy(&ie->data[i], tmp->apdu, tmp->apdu_len); i += tmp->apdu_len; tmp->sent = 1; return i + 2; } static FUNC_RECV(receive_facility) { int i = 0; int protocol, next_protocol; struct rose_component *comp = NULL; enum { Q932_STATE_NFE, /* Network facility extension */ Q932_STATE_NPP, /* Network protocol profile */ Q932_STATE_INTERPRETATION, /* Interpretation component */ Q932_STATE_SERVICE /* Service component(s) */ } state = Q932_STATE_SERVICE; #define Q932_HANDLE_PROC(component, my_state, name, handler) \ case component: \ if(state > my_state) { \ pri_error(pri, "!! %s component received in wrong place\n"); \ break; \ } \ state = my_state; \ if (pri->debug) \ pri_message(pri, "Handle Q.932 %s component\n", name); \ (handler)(pri, call, comp->data, comp->len); \ break; #define Q932_HANDLE_NULL(component, my_state, name, handle) \ case component: \ if(state > my_state) { \ pri_error(pri, "!! %s component received in wrong place\n"); \ break; \ } \ state = my_state; \ if (pri->debug & PRI_DEBUG_APDU) \ pri_message(pri, "Q.932 %s component is not handled\n", name); \ break; if (ie->len < 1) return -1; switch(next_protocol = protocol = (ie->data[i] & 0x1f)) { case Q932_PROTOCOL_CMIP: case Q932_PROTOCOL_ACSE: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "!! Don't know how to handle Q.932 Protocol Profile of type 0x%X\n", protocol); return -1; case Q932_PROTOCOL_EXTENSIONS: state = Q932_STATE_NFE; next_protocol = Q932_PROTOCOL_ROSE; break; case Q932_PROTOCOL_ROSE: break; default: pri_error(pri, "!! Invalid Q.932 Protocol Profile of type 0x%X received\n", protocol); return -1; } /* Service indicator octet - Just ignore for now */ if (!(ie->data[i] & 0x80)) i++; i++; if (ie->len < 3) return -1; while ((i+1 < ie->len) && (&ie->data[i])) { comp = (struct rose_component*)&ie->data[i]; if (comp->type) { if (protocol == Q932_PROTOCOL_EXTENSIONS) { switch (comp->type) { Q932_HANDLE_NULL(COMP_TYPE_INTERPRETATION, Q932_STATE_INTERPRETATION, "Interpretation", NULL); Q932_HANDLE_NULL(COMP_TYPE_NFE, Q932_STATE_NFE, "Network facility extensions", NULL); Q932_HANDLE_NULL(COMP_TYPE_NETWORK_PROTOCOL_PROFILE, Q932_STATE_NPP, "Network protocol profile", NULL); default: protocol = next_protocol; break; } } switch (protocol) { case Q932_PROTOCOL_ROSE: switch (comp->type) { Q932_HANDLE_PROC(COMP_TYPE_INVOKE, Q932_STATE_SERVICE, "ROSE Invoke", rose_invoke_decode); Q932_HANDLE_PROC(COMP_TYPE_RETURN_RESULT, Q932_STATE_SERVICE, "ROSE return result", rose_return_result_decode); Q932_HANDLE_PROC(COMP_TYPE_RETURN_ERROR, Q932_STATE_SERVICE, "ROSE return error", rose_return_error_decode); Q932_HANDLE_PROC(COMP_TYPE_REJECT, Q932_STATE_SERVICE, "ROSE reject", rose_reject_decode); default: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Don't know how to handle ROSE component of type 0x%X\n", comp->type); break; } break; case Q932_PROTOCOL_CMIP: switch (comp->type) { default: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Don't know how to handle CMIP component of type 0x%X\n", comp->type); break; } break; case Q932_PROTOCOL_ACSE: switch (comp->type) { default: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Don't know how to handle ACSE component of type 0x%X\n", comp->type); break; } break; } } i += (comp->len + 2); } #undef Q932_HANDLE return 0; } static FUNC_SEND(transmit_progress_indicator) { int code, mask; /* Can't send progress indicator on GR-303 -- EVER! */ if (pri->subchannel) return 0; if (call->progressmask > 0) { if (call->progressmask & (mask = PRI_PROG_CALL_NOT_E2E_ISDN)) code = Q931_PROG_CALL_NOT_E2E_ISDN; else if (call->progressmask & (mask = PRI_PROG_CALLED_NOT_ISDN)) code = Q931_PROG_CALLED_NOT_ISDN; else if (call->progressmask & (mask = PRI_PROG_CALLER_NOT_ISDN)) code = Q931_PROG_CALLER_NOT_ISDN; else if (call->progressmask & (mask = PRI_PROG_INBAND_AVAILABLE)) code = Q931_PROG_INBAND_AVAILABLE; else if (call->progressmask & (mask = PRI_PROG_DELAY_AT_INTERF)) code = Q931_PROG_DELAY_AT_INTERF; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_WITH_PUBLIC)) code = Q931_PROG_INTERWORKING_WITH_PUBLIC; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE)) code = Q931_PROG_INTERWORKING_NO_RELEASE; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER)) code = Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER; else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER)) code = Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER; else { code = 0; pri_error(pri, "XXX Undefined progress bit: %x\n", call->progressmask); } if (code) { ie->data[0] = 0x80 | (call->progcode << 5) | (call->progloc); ie->data[1] = 0x80 | code; call->progressmask &= ~mask; return 4; } } /* Leave off */ return 0; } static FUNC_SEND(transmit_call_state) { if (call->ourcallstate > -1 ) { ie->data[0] = call->ourcallstate; return 3; } return 0; } static FUNC_RECV(receive_call_state) { call->sugcallstate = ie->data[0] & 0x3f; return 0; } static char *callstate2str(int callstate) { static struct msgtype callstates[] = { { 0, "Null" }, { 1, "Call Initiated" }, { 2, "Overlap sending" }, { 3, "Outgoing call Proceeding" }, { 4, "Call Delivered" }, { 6, "Call Present" }, { 7, "Call Received" }, { 8, "Connect Request" }, { 9, "Incoming Call Proceeding" }, { 10, "Active" }, { 11, "Disconnect Request" }, { 12, "Disconnect Indication" }, { 15, "Suspend Request" }, { 17, "Resume Request" }, { 19, "Release Request" }, { 22, "Call Abort" }, { 25, "Overlap Receiving" }, { 61, "Restart Request" }, { 62, "Restart" }, }; return code2str(callstate, callstates, sizeof(callstates) / sizeof(callstates[0])); } static FUNC_DUMP(dump_call_state) { pri_message(pri, "%c Call State (len=%2d) [ Ext: %d Coding: %s (%d) Call state: %s (%d)\n", prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0xC0) >> 6), (ie->data[0] & 0xC0) >> 6, callstate2str(ie->data[0] & 0x3f), ie->data[0] & 0x3f); } static FUNC_DUMP(dump_call_identity) { int x; pri_message(pri, "%c Call Identity (len=%2d) [ ", prefix, len); for (x=0;xlen;x++) pri_message(pri, "0x%02X ", ie->data[x]); pri_message(pri, " ]\n"); } static FUNC_DUMP(dump_time_date) { pri_message(pri, "%c Time Date (len=%2d) [ ", prefix, len); if (ie->len > 0) pri_message(pri, "%02d", ie->data[0]); if (ie->len > 1) pri_message(pri, "-%02d", ie->data[1]); if (ie->len > 2) pri_message(pri, "-%02d", ie->data[2]); if (ie->len > 3) pri_message(pri, " %02d", ie->data[3]); if (ie->len > 4) pri_message(pri, ":%02d", ie->data[4]); if (ie->len > 5) pri_message(pri, ":%02d", ie->data[5]); pri_message(pri, " ]\n"); } static FUNC_DUMP(dump_keypad_facility) { char tmp[64]; if (ie->len == 0 || ie->len > sizeof(tmp)) return; memcpy(tmp, ie->data, ie->len); tmp[ie->len] = '\0'; pri_message(pri, "%c Keypad Facility (len=%2d) [ %s ]\n", prefix, ie->len, tmp ); } static FUNC_RECV(receive_keypad_facility) { int mylen; if (ie->len == 0) return -1; if (ie->len > (sizeof(call->keypad_digits) - 1)) mylen = (sizeof(call->keypad_digits) - 1); else mylen = ie->len; memcpy(call->keypad_digits, ie->data, mylen); call->keypad_digits[mylen] = 0; return 0; } static FUNC_SEND(transmit_keypad_facility) { int sublen; sublen = strlen(call->keypad_digits); if (sublen > 32) { sublen = 32; call->keypad_digits[32] = '\0'; } if (sublen) { libpri_copy_string((char *)ie->data, (char *)call->keypad_digits, sizeof(call->keypad_digits)); /* Make sure we clear the field */ call->keypad_digits[0] = '\0'; return sublen + 2; } else return 0; } static FUNC_DUMP(dump_display) { int x, y; char *buf = malloc(len + 1); char tmp[80] = ""; if (buf) { x=y=0; if ((x < ie->len) && (ie->data[x] & 0x80)) { sprintf(tmp, "Charset: %02x ", ie->data[x] & 0x7f); ++x; } for (y=x; xlen; x++) buf[x] = ie->data[x] & 0x7f; buf[x] = '\0'; pri_message(pri, "%c Display (len=%2d) %s[ %s ]\n", prefix, ie->len, tmp, &buf[y]); free(buf); } } #define CHECK_OVERFLOW(limit) \ if (tmpptr - tmp + limit >= sizeof(tmp)) { \ *tmpptr = '\0'; \ pri_message(pri, "%s", tmpptr = tmp); \ } static void dump_ie_data(struct pri *pri, unsigned char *c, int len) { static char hexs[16] = "0123456789ABCDEF"; char tmp[1024], *tmpptr; int lastascii = 0; tmpptr = tmp; for (; len; --len, ++c) { CHECK_OVERFLOW(7); if (isprint(*c)) { if (!lastascii) { if (tmpptr != tmp) { *tmpptr++ = ','; *tmpptr++ = ' '; } *tmpptr++ = '\''; lastascii = 1; } *tmpptr++ = *c; } else { if (lastascii) { *tmpptr++ = '\''; lastascii = 0; } if (tmpptr != tmp) { *tmpptr++ = ','; *tmpptr++ = ' '; } *tmpptr++ = '0'; *tmpptr++ = 'x'; *tmpptr++ = hexs[(*c >> 4) & 0x0f]; *tmpptr++ = hexs[(*c) & 0x0f]; } } if (lastascii) *tmpptr++ = '\''; *tmpptr = '\0'; pri_message(pri, "%s", tmp); } static FUNC_DUMP(dump_facility) { int dataat = (ie->data[0] & 0x80) ? 1 : 2; pri_message(pri, "%c Facility (len=%2d, codeset=%d) [ ", prefix, len, Q931_IE_CODESET(full_ie)); dump_ie_data(pri, ie->data, ie->len); pri_message(NULL, " ]\n"); if (ie->len > 1) { pri_message(pri, "PROTOCOL %02X\n", ie->data[0] & ASN1_TYPE_MASK); asn1_dump(pri, &ie->data[dataat], ie->len - dataat); } } static FUNC_DUMP(dump_network_spec_fac) { pri_message(pri, "%c Network-Specific Facilities (len=%2d) [ ", prefix, ie->len); if (ie->data[0] == 0x00) { pri_message(pri, "%s", code2str(ie->data[1], facilities, sizeof(facilities) / sizeof(facilities[0]))); } else dump_ie_data(pri, ie->data, ie->len); pri_message(pri, " ]\n"); } static FUNC_RECV(receive_network_spec_fac) { return 0; } static FUNC_SEND(transmit_network_spec_fac) { /* We are ready to transmit single IE only */ if (order > 1) return 0; if (pri->nsf != PRI_NSF_NONE) { ie->data[0] = 0x00; ie->data[1] = pri->nsf; return 4; } /* Leave off */ return 0; } char *pri_cause2str(int cause) { return code2str(cause, causes, sizeof(causes) / sizeof(causes[0])); } static char *pri_causeclass2str(int cause) { static struct msgtype causeclasses[] = { { 0, "Normal Event" }, { 1, "Normal Event" }, { 2, "Network Congestion (resource unavailable)" }, { 3, "Service or Option not Available" }, { 4, "Service or Option not Implemented" }, { 5, "Invalid message (e.g. parameter out of range)" }, { 6, "Protocol Error (e.g. unknown message)" }, { 7, "Interworking" }, }; return code2str(cause, causeclasses, sizeof(causeclasses) / sizeof(causeclasses[0])); } static FUNC_DUMP(dump_cause) { int x; pri_message(pri, "%c Cause (len=%2d) [ Ext: %d Coding: %s (%d) Spare: %d Location: %s (%d)\n", prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5, (ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf); pri_message(pri, "%c Ext: %d Cause: %s (%d), class = %s (%d) ]\n", prefix, (ie->data[1] >> 7), pri_cause2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, pri_causeclass2str((ie->data[1] & 0x7f) >> 4), (ie->data[1] & 0x7f) >> 4); if (ie->len < 3) return; /* Dump cause data in readable form */ switch(ie->data[1] & 0x7f) { case PRI_CAUSE_IE_NONEXIST: for (x=2;xlen;x++) pri_message(pri, "%c Cause data %d: %02x (%d, %s IE)\n", prefix, x-1, ie->data[x], ie->data[x], ie2str(ie->data[x])); break; case PRI_CAUSE_WRONG_CALL_STATE: for (x=2;xlen;x++) pri_message(pri, "%c Cause data %d: %02x (%d, %s message)\n", prefix, x-1, ie->data[x], ie->data[x], msg2str(ie->data[x])); break; case PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE: pri_message(pri, "%c Cause data:", prefix); for (x=2;xlen;x++) pri_message(pri, " %02x", ie->data[x]); pri_message(pri, " (Timer T"); for (x=2;xlen;x++) pri_message(pri, "%c", ((ie->data[x] >= ' ') && (ie->data[x] < 0x7f)) ? ie->data[x] : '.'); pri_message(pri, ")\n"); break; default: for (x=2;xlen;x++) pri_message(pri, "%c Cause data %d: %02x (%d)\n", prefix, x-1, ie->data[x], ie->data[x]); break; } } static FUNC_RECV(receive_cause) { call->causeloc = ie->data[0] & 0xf; call->causecode = (ie->data[0] & 0x60) >> 5; call->cause = (ie->data[1] & 0x7f); return 0; } static FUNC_SEND(transmit_cause) { /* We are ready to transmit single IE only */ if (order > 1) return 0; if (call->cause > 0) { ie->data[0] = 0x80 | (call->causecode << 5) | (call->causeloc); ie->data[1] = 0x80 | (call->cause); return 4; } else { /* Leave off */ return 0; } } static FUNC_DUMP(dump_sending_complete) { pri_message(pri, "%c Sending Complete (len=%2d)\n", prefix, len); } static FUNC_RECV(receive_sending_complete) { /* We've got a "Complete" message: Exect no further digits. */ call->complete = 1; return 0; } static FUNC_SEND(transmit_sending_complete) { if ((pri->overlapdial && call->complete) || /* Explicit */ (!pri->overlapdial && ((pri->switchtype == PRI_SWITCH_EUROISDN_E1) || /* Implicit */ (pri->switchtype == PRI_SWITCH_EUROISDN_T1)))) { /* Include this single-byte IE */ return 1; } return 0; } static char *notify2str(int info) { /* ITU-T Q.763 */ static struct msgtype notifies[] = { { PRI_NOTIFY_USER_SUSPENDED, "User suspended" }, { PRI_NOTIFY_USER_RESUMED, "User resumed" }, { PRI_NOTIFY_BEARER_CHANGE, "Bearer service change (DSS1)" }, { PRI_NOTIFY_ASN1_COMPONENT, "ASN.1 encoded component (DSS1)" }, { PRI_NOTIFY_COMPLETION_DELAY, "Call completion delay" }, { PRI_NOTIFY_CONF_ESTABLISHED, "Conference established" }, { PRI_NOTIFY_CONF_DISCONNECTED, "Conference disconnected" }, { PRI_NOTIFY_CONF_PARTY_ADDED, "Other party added" }, { PRI_NOTIFY_CONF_ISOLATED, "Isolated" }, { PRI_NOTIFY_CONF_REATTACHED, "Reattached" }, { PRI_NOTIFY_CONF_OTHER_ISOLATED, "Other party isolated" }, { PRI_NOTIFY_CONF_OTHER_REATTACHED, "Other party reattached" }, { PRI_NOTIFY_CONF_OTHER_SPLIT, "Other party split" }, { PRI_NOTIFY_CONF_OTHER_DISCONNECTED, "Other party disconnected" }, { PRI_NOTIFY_CONF_FLOATING, "Conference floating" }, { PRI_NOTIFY_WAITING_CALL, "Call is waiting call" }, { PRI_NOTIFY_DIVERSION_ACTIVATED, "Diversion activated (DSS1)" }, { PRI_NOTIFY_TRANSFER_ALERTING, "Call transfer, alerting" }, { PRI_NOTIFY_TRANSFER_ACTIVE, "Call transfer, active" }, { PRI_NOTIFY_REMOTE_HOLD, "Remote hold" }, { PRI_NOTIFY_REMOTE_RETRIEVAL, "Remote retrieval" }, { PRI_NOTIFY_CALL_DIVERTING, "Call is diverting" }, }; return code2str(info, notifies, sizeof(notifies) / sizeof(notifies[0])); } static FUNC_DUMP(dump_notify) { pri_message(pri, "%c Notification indicator (len=%2d): Ext: %d %s (%d)\n", prefix, len, ie->data[0] >> 7, notify2str(ie->data[0] & 0x7f), ie->data[0] & 0x7f); } static FUNC_RECV(receive_notify) { call->notify = ie->data[0] & 0x7F; return 0; } static FUNC_SEND(transmit_notify) { if (call->notify >= 0) { ie->data[0] = 0x80 | call->notify; return 3; } return 0; } static FUNC_DUMP(dump_shift) { pri_message(pri, "%c %sLocking Shift (len=%02d): Requested codeset %d\n", prefix, (full_ie & 8) ? "Non-" : "", len, full_ie & 7); } static char *lineinfo2str(int info) { /* NAPNA ANI II digits */ static struct msgtype lineinfo[] = { { 0, "Plain Old Telephone Service (POTS)" }, { 1, "Multiparty line (more than 2)" }, { 2, "ANI failure" }, { 6, "Station Level Rating" }, { 7, "Special Operator Handling Required" }, { 20, "Automatic Identified Outward Dialing (AIOD)" }, { 23, "Coing or Non-Coin" }, { 24, "Toll free translated to POTS originated for non-pay station" }, { 25, "Toll free translated to POTS originated from pay station" }, { 27, "Pay station with coin control signalling" }, { 29, "Prison/Inmate Service" }, { 30, "Intercept (blank)" }, { 31, "Intercept (trouble)" }, { 32, "Intercept (regular)" }, { 34, "Telco Operator Handled Call" }, { 52, "Outward Wide Area Telecommunications Service (OUTWATS)" }, { 60, "TRS call from unrestricted line" }, { 61, "Cellular/Wireless PCS (Type 1)" }, { 62, "Cellular/Wireless PCS (Type 2)" }, { 63, "Cellular/Wireless PCS (Roaming)" }, { 66, "TRS call from hotel/motel" }, { 67, "TRS call from restricted line" }, { 70, "Line connected to pay station" }, { 93, "Private virtual network call" }, }; return code2str(info, lineinfo, sizeof(lineinfo) / sizeof(lineinfo[0])); } static FUNC_DUMP(dump_line_information) { pri_message(pri, "%c Originating Line Information (len=%02d): %s (%d)\n", prefix, len, lineinfo2str(ie->data[0]), ie->data[0]); } static FUNC_RECV(receive_line_information) { call->ani2 = ie->data[0]; return 0; } static FUNC_SEND(transmit_line_information) { #if 0 /* XXX Is this IE possible for 4ESS only? XXX */ if(pri->switchtype == PRI_SWITCH_ATT4ESS) { ie->data[0] = 0; return 3; } #endif return 0; } static char *gdencoding2str(int encoding) { static struct msgtype gdencoding[] = { { 0, "BCD even" }, { 1, "BCD odd" }, { 2, "IA5" }, { 3, "Binary" }, }; return code2str(encoding, gdencoding, sizeof(gdencoding) / sizeof(gdencoding[0])); } static char *gdtype2str(int type) { static struct msgtype gdtype[] = { { 0, "Account Code" }, { 1, "Auth Code" }, { 2, "Customer ID" }, { 3, "Universal Access" }, { 4, "Info Digits" }, { 5, "Callid" }, { 6, "Opart" }, { 7, "TCN" }, { 9, "Adin" }, }; return code2str(type, gdtype, sizeof(gdtype) / sizeof(gdtype[0])); } static FUNC_DUMP(dump_generic_digits) { int encoding; int type; int idx; int value; if (len < 3) { pri_message(pri, "%c Generic Digits (len=%02d): Invalid length\n", prefix, len); return; } encoding = (ie->data[0] >> 5) & 7; type = ie->data[0] & 0x1F; pri_message(pri, "%c Generic Digits (len=%02d): Encoding %s Type %s\n", prefix, len, gdencoding2str(encoding), gdtype2str(type)); if (encoding == 3) { /* Binary */ pri_message(pri, "%c Don't know how to handle binary encoding\n"); return; } if (len == 3) /* No number information */ return; pri_message(pri, "%c Digits: "); value = 0; for(idx = 3; idx < len; ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ pri_message(pri, "%d", ie->data[idx-2] & 0x0f); value = value * 10 + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) { /* Special handling for BCD odd */ pri_message(pri, "%d", (ie->data[idx-2] >> 4) & 0x0f); value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f); } break; case 2: /* IA5 */ pri_message(pri, "%c", ie->data[idx-2]); value = value * 10 + ie->data[idx-2] - '0'; break; } } switch(type) { case 4: /* Info Digits */ pri_message(pri, " - %s", lineinfo2str(value)); break; } pri_message(pri, "\n"); } static FUNC_RECV(receive_generic_digits) { int encoding; int type; int idx; int value; int num_idx; char number[260]; if (len < 3) { pri_error(pri, "Invalid length of Generic Digits IE\n"); return -1; } encoding = (ie->data[0] >> 5) & 7; type = ie->data[0] & 0x1F; if (encoding == 3) { /* Binary */ pri_message(pri, "!! Unable to handle binary encoded Generic Digits IE\n"); return 0; } if (len == 3) /* No number information */ return 0; value = 0; switch(type) { /* Integer value handling */ case 4: /* Info Digits */ for(idx = 3; idx < len; ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ value = value * 10 + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) /* Special handling for BCD odd */ value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f); break; case 2: /* IA5 */ value = value * 10 + (ie->data[idx-2] - '0'); break; } } break; /* String value handling */ case 5: /* Callid */ num_idx = 0; for(idx = 3; (idx < len) && (num_idx < sizeof(number) - 4); ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ number[num_idx++] = '0' + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) /* Special handling for BCD odd */ number[num_idx++] = '0' + ((ie->data[idx-2] >> 4) & 0x0f); break; case 2: number[num_idx++] = ie->data[idx-2]; break; } } number[num_idx] = '\0'; break; } switch(type) { case 4: /* Info Digits */ call->ani2 = value; break; #if 0 case 5: /* Callid */ if (!call->callernum[0]) { memcpy(call->callernum, number, sizeof(call->callernum)-1); call->callerpres = 0; call->callerplan = 0; } break; #endif } return 0; } static FUNC_SEND(transmit_generic_digits) { #if 0 /* XXX Is this IE possible for other switches? XXX */ if (order > 1) return 0; if(pri->switchtype == PRI_SWITCH_NI1) { ie->data[0] = 0x04; /* BCD even, Info Digits */ ie->data[1] = 0x00; /* POTS */ return 4; } #endif return 0; } static char *signal2str(int signal) { /* From Q.931 4.5.8 Table 4-24 */ static struct msgtype mtsignal[] = { { 0, "Dial tone" }, { 1, "Ring back tone" }, { 2, "Intercept tone" }, { 3, "Network congestion tone" }, { 4, "Busy tone" }, { 5, "Confirm tone" }, { 6, "Answer tone" }, { 7, "Call waiting tone" }, { 8, "Off-hook warning tone" }, { 9, "Pre-emption tone" }, { 63, "Tones off" }, { 64, "Alerting on - pattern 0" }, { 65, "Alerting on - pattern 1" }, { 66, "Alerting on - pattern 2" }, { 67, "Alerting on - pattern 3" }, { 68, "Alerting on - pattern 4" }, { 69, "Alerting on - pattern 5" }, { 70, "Alerting on - pattern 6" }, { 71, "Alerting on - pattern 7" }, { 79, "Alerting off" }, }; return code2str(signal, mtsignal, sizeof(mtsignal) / sizeof(mtsignal[0])); } static FUNC_DUMP(dump_signal) { pri_message(pri, "%c Signal (len=%02d): ", prefix, len); if (len < 3) { pri_message(pri, "Invalid length\n"); return; } pri_message(pri, "Signal %s (%d)\n", signal2str(ie->data[0]), ie->data[0]); } static FUNC_DUMP(dump_transit_count) { /* Defined in ECMA-225 */ pri_message(pri, "%c Transit Count (len=%02d): ", prefix, len); if (len < 3) { pri_message(pri, "Invalid length\n"); return; } pri_message(pri, "Count=%d (0x%02x)\n", ie->data[0] & 0x1f, ie->data[0] & 0x1f); } static struct ie ies[] = { /* Codeset 0 - Common */ { 1, NATIONAL_CHANGE_STATUS, "Change Status" }, { 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift }, { 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability }, { 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause }, { 1, Q931_CALL_STATE, "Call State", dump_call_state, receive_call_state, transmit_call_state }, { 0, Q931_CHANNEL_IDENT, "Channel Identification", dump_channel_id, receive_channel_id, transmit_channel_id }, { 0, Q931_PROGRESS_INDICATOR, "Progress Indicator", dump_progress_indicator, receive_progress_indicator, transmit_progress_indicator }, { 0, Q931_NETWORK_SPEC_FAC, "Network-Specific Facilities", dump_network_spec_fac, receive_network_spec_fac, transmit_network_spec_fac }, { 1, Q931_INFORMATION_RATE, "Information Rate" }, { 1, Q931_TRANSIT_DELAY, "End-to-End Transit Delay" }, { 1, Q931_TRANS_DELAY_SELECT, "Transmit Delay Selection and Indication" }, { 1, Q931_BINARY_PARAMETERS, "Packet-layer Binary Parameters" }, { 1, Q931_WINDOW_SIZE, "Packet-layer Window Size" }, { 1, Q931_CLOSED_USER_GROUP, "Closed User Group" }, { 1, Q931_REVERSE_CHARGE_INDIC, "Reverse Charging Indication" }, { 1, Q931_CALLING_PARTY_NUMBER, "Calling Party Number", dump_calling_party_number, receive_calling_party_number, transmit_calling_party_number }, { 1, Q931_CALLING_PARTY_SUBADDR, "Calling Party Subaddress", dump_calling_party_subaddr, receive_calling_party_subaddr }, { 1, Q931_CALLED_PARTY_NUMBER, "Called Party Number", dump_called_party_number, receive_called_party_number, transmit_called_party_number }, { 1, Q931_CALLED_PARTY_SUBADDR, "Called Party Subaddress", dump_called_party_subaddr }, { 0, Q931_REDIRECTING_NUMBER, "Redirecting Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_REDIRECTING_SUBADDR, "Redirecting Subaddress", dump_redirecting_subaddr }, { 0, Q931_TRANSIT_NET_SELECT, "Transit Network Selection" }, { 1, Q931_RESTART_INDICATOR, "Restart Indicator", dump_restart_indicator, receive_restart_indicator, transmit_restart_indicator }, { 0, Q931_LOW_LAYER_COMPAT, "Low-layer Compatibility" }, { 0, Q931_HIGH_LAYER_COMPAT, "High-layer Compatibility" }, { 1, Q931_PACKET_SIZE, "Packet Size" }, { 0, Q931_IE_FACILITY, "Facility" , dump_facility, receive_facility, transmit_facility }, { 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number" }, { 1, Q931_IE_REDIRECTION_SUBADDR, "Redirection Subaddress" }, { 1, Q931_IE_FEATURE_ACTIVATE, "Feature Activation" }, { 1, Q931_IE_INFO_REQUEST, "Feature Request" }, { 1, Q931_IE_FEATURE_IND, "Feature Indication" }, { 1, Q931_IE_SEGMENTED_MSG, "Segmented Message" }, { 1, Q931_IE_CALL_IDENTITY, "Call Identity", dump_call_identity }, { 1, Q931_IE_ENDPOINT_ID, "Endpoint Identification" }, { 1, Q931_IE_NOTIFY_IND, "Notification Indicator", dump_notify, receive_notify, transmit_notify }, { 1, Q931_DISPLAY, "Display", dump_display, receive_display, transmit_display }, { 1, Q931_IE_TIME_DATE, "Date/Time", dump_time_date }, { 1, Q931_IE_KEYPAD_FACILITY, "Keypad Facility", dump_keypad_facility, receive_keypad_facility, transmit_keypad_facility }, { 0, Q931_IE_SIGNAL, "Signal", dump_signal }, { 1, Q931_IE_SWITCHHOOK, "Switch-hook" }, { 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user, transmit_user_user }, { 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" }, { 1, Q931_IE_CALL_STATUS, "Call Status" }, { 1, Q931_IE_CHANGE_STATUS, "Change Status" }, { 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number }, { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number }, { 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_IE_USER_USER_FACILITY, "User-User Facility" }, { 1, Q931_IE_UPDATE, "Update" }, { 1, Q931_SENDING_COMPLETE, "Sending Complete", dump_sending_complete, receive_sending_complete, transmit_sending_complete }, /* Codeset 4 - Q.SIG specific */ { 1, QSIG_IE_TRANSIT_COUNT | Q931_CODESET(4), "Transit Count", dump_transit_count }, /* Codeset 6 - Network specific */ { 1, Q931_IE_ORIGINATING_LINE_INFO, "Originating Line Information", dump_line_information, receive_line_information, transmit_line_information }, { 1, Q931_IE_FACILITY | Q931_CODESET(6), "Facility", dump_facility, receive_facility, transmit_facility }, { 1, Q931_DISPLAY | Q931_CODESET(6), "Display (CS6)", dump_display, receive_display, transmit_display }, { 0, Q931_IE_GENERIC_DIGITS, "Generic Digits", dump_generic_digits, receive_generic_digits, transmit_generic_digits }, /* Codeset 7 */ }; static char *ie2str(int ie) { unsigned int x; /* Special handling for Locking/Non-Locking Shifts */ switch (ie & 0xf8) { case Q931_LOCKING_SHIFT: switch (ie & 7) { case 0: return "!! INVALID Locking Shift To Codeset 0"; case 1: return "Locking Shift To Codeset 1"; case 2: return "Locking Shift To Codeset 2"; case 3: return "Locking Shift To Codeset 3"; case 4: return "Locking Shift To Codeset 4"; case 5: return "Locking Shift To Codeset 5"; case 6: return "Locking Shift To Codeset 6"; case 7: return "Locking Shift To Codeset 7"; } case Q931_NON_LOCKING_SHIFT: switch (ie & 7) { case 0: return "Non-Locking Shift To Codeset 0"; case 1: return "Non-Locking Shift To Codeset 1"; case 2: return "Non-Locking Shift To Codeset 2"; case 3: return "Non-Locking Shift To Codeset 3"; case 4: return "Non-Locking Shift To Codeset 4"; case 5: return "Non-Locking Shift To Codeset 5"; case 6: return "Non-Locking Shift To Codeset 6"; case 7: return "Non-Locking Shift To Codeset 7"; } default: for (x=0;xie & 0x80) != 0) return 1; else return 2 + ie->len; } static char *msg2str(int msg) { unsigned int x; for (x=0;xcrlen > 3) { pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen); return -1; } switch (h->crlen) { case 2: for (x=0;xcrlen;x++) { cr <<= 8; cr |= h->crv[x]; } break; case 1: cr = h->crv[0]; if (cr & 0x80) { cr &= ~0x80; cr |= 0x8000; } break; default: pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); } return cr; } static inline void q931_dumpie(struct pri *pri, int codeset, q931_ie *ie, char prefix) { unsigned int x; int full_ie = Q931_FULL_IE(codeset, ie->ie); int base_ie; char *buf = malloc(ielen(ie) * 3 + 1); int buflen = 0; buf[0] = '\0'; if (!(ie->ie & 0x80)) { buflen += sprintf(buf, " %02x", ielen(ie)-2); for (x = 0; x + 2 < ielen(ie); ++x) buflen += sprintf(buf + buflen, " %02x", ie->data[x]); } pri_message(pri, "%c [%02x%s]\n", prefix, ie->ie, buf); free(buf); /* Special treatment for shifts */ if((full_ie & 0xf0) == Q931_LOCKING_SHIFT) full_ie &= 0xff; base_ie = (((full_ie & ~0x7f) == Q931_FULL_IE(0, 0x80)) && ((full_ie & 0x70) != 0x20)) ? full_ie & ~0x0f : full_ie; for (x=0;xcallpool; prev = NULL; while(cur) { if (cur->cr == cr) return cur; prev = cur; cur = cur->next; } /* No call exists, make a new one */ if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "-- Making new call for cr %d\n", cr); cur = malloc(sizeof(struct q931_call)); if (cur) { call_init(cur); /* Call reference */ cur->cr = cr; cur->pri = pri; /* Append to end of list */ if (prev) prev->next = cur; else *pri->callpool = cur; } return cur; } q931_call *q931_new_call(struct pri *pri) { q931_call *cur; do { cur = *pri->callpool; pri->cref++; if (pri->cref > 32767) pri->cref = 1; while(cur) { if (cur->cr == (0x8000 | pri->cref)) break; cur = cur->next; } } while(cur); return q931_getcall(pri, pri->cref | 0x8000); } static void q931_destroy(struct pri *pri, int cr, q931_call *c) { q931_call *cur, *prev; prev = NULL; cur = *pri->callpool; while(cur) { if ((c && (cur == c)) || (!c && (cur->cr == cr))) { if (prev) prev->next = cur->next; else *pri->callpool = cur->next; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n",callstate2str(cur->ourcallstate),callstate2str(cur->peercallstate)); if (cur->retranstimer) pri_schedule_del(pri, cur->retranstimer); pri_call_apdu_queue_cleanup(cur); free(cur); return; } prev = cur; cur = cur->next; } pri_error(pri, "Can't destroy call %d!\n", cr); } static void q931_destroycall(struct pri *pri, int cr) { return q931_destroy(pri, cr, NULL); } void __q931_destroycall(struct pri *pri, q931_call *c) { if (pri && c) q931_destroy(pri,0, c); return; } static int add_ie(struct pri *pri, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset) { unsigned int x; int res, total_res; int have_shift; int ies_count, order; for (x=0;xie = Q931_IE_CODESET(ies[x].ie) | (Q931_IE_CODESET(ies[x].ie) ? Q931_LOCKING_SHIFT : Q931_NON_LOCKING_SHIFT); have_shift = 1; iet = (q931_ie *)((char *)iet + 1); maxlen--; } else have_shift = 0; ies_count = ies[x].max_count; if (ies_count == 0) ies_count = INT_MAX; order = 0; total_res = 0; do { iet->ie = ie; res = ies[x].transmit(ie, pri, call, msgtype, iet, maxlen, ++order); /* Error if res < 0 or ignored if res == 0 */ if (res < 0) return res; if (res > 0) { if ((iet->ie & 0x80) == 0) /* Multibyte IE */ iet->len = res - 2; total_res += res; maxlen -= res; iet = (q931_ie *)((char *)iet + res); } } while (res > 0 && order < ies_count); if (have_shift && total_res) { if (Q931_IE_CODESET(ies[x].ie)) *codeset = Q931_IE_CODESET(ies[x].ie); return total_res + 1; /* Shift is single-byte IE */ } return total_res; } else { pri_error(pri, "!! Don't know how to add an IE %s (%d)\n", ie2str(ie), ie); return -1; } } } pri_error(pri, "!! Unknown IE %d (%s)\n", ie, ie2str(ie)); return -1; } static char *disc2str(int disc) { static struct msgtype discs[] = { { Q931_PROTOCOL_DISCRIMINATOR, "Q.931" }, { GR303_PROTOCOL_DISCRIMINATOR, "GR-303" }, { 0x3, "AT&T Maintenance" }, { 0x43, "New AT&T Maintenance" }, }; return code2str(disc, discs, sizeof(discs) / sizeof(discs[0])); } void q931_dump(struct pri *pri, q931_h *h, int len, int txrx) { q931_mh *mh; char c; int x=0, r; int cur_codeset; int codeset; c = txrx ? '>' : '<'; pri_message(pri, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len); pri_message(pri, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", c, h->crlen, q931_cr(h) & 0x7FFF, q931_cr(h) & 0x7FFF, (h->crv[0] & 0x80) ? "Terminator" : "Originator"); /* Message header begins at the end of the call reference number */ mh = (q931_mh *)(h->contents + h->crlen); pri_message(pri, "%c Message type: %s (%d)\n", c, msg2str(mh->msg), mh->msg); /* Drop length of header, including call reference */ len -= (h->crlen + 3); codeset = cur_codeset = 0; while(x < len) { r = ielen((q931_ie *)(mh->data + x)); q931_dumpie(pri, cur_codeset, (q931_ie *)(mh->data + x), c); switch (mh->data[x] & 0xf8) { case Q931_LOCKING_SHIFT: if ((mh->data[x] & 7) > 0) codeset = cur_codeset = mh->data[x] & 7; break; case Q931_NON_LOCKING_SHIFT: cur_codeset = mh->data[x] & 7; break; default: /* Reset temporary codeset change */ cur_codeset = codeset; } x += r; } if (x > len) pri_error(pri, "XXX Message longer than it should be?? XXX\n"); } static int q931_handle_ie(int codeset, struct pri *pri, q931_call *c, int msg, q931_ie *ie) { unsigned int x; int full_ie = Q931_FULL_IE(codeset, ie->ie); if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "-- Processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); for (x=0;xdebug & PRI_DEBUG_Q931_ANOMALY) pri_error(pri, "!! No handler for IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); return -1; } } } pri_message(pri, "!! Unknown IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); return -1; } static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len) { /* Returns header and message header and modifies length in place */ q931_h *h = (q931_h *)buf; q931_mh * mh = (q931_mh *)(h->contents + 2); h->pd = pri->protodisc; h->x0 = 0; /* Reserved 0 */ h->crlen = 2; /* Two bytes of Call Reference. Invert the top bit to make it from our sense */ if (call->cr || call->forceinvert) { h->crv[0] = ((call->cr ^ 0x8000) & 0xff00) >> 8; h->crv[1] = (call->cr & 0xff); } else { /* Unless of course this has no call reference */ h->crv[0] = 0; h->crv[1] = 0; } if (pri->subchannel) { /* On GR-303, top bit is always 0 */ h->crv[0] &= 0x7f; } mh->f = 0; *hb = h; *mhb = mh; *len -= 5; } static int q931_xmit(struct pri *pri, q931_h *h, int len, int cr) { q921_transmit_iframe(pri, h, len, cr); /* The transmit operation might dump the q921 header, so logging the q931 message body after the transmit puts the sections of the message in the right order in the log */ if (pri->debug & PRI_DEBUG_Q931_DUMP) q931_dump(pri, h, len, 1); #ifdef LIBPRI_COUNTERS pri->q931_txcount++; #endif return 0; } static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[]) { unsigned char buf[1024]; q931_h *h; q931_mh *mh; int len; int res; int offset=0; int x; int codeset; memset(buf, 0, sizeof(buf)); len = sizeof(buf); init_header(pri, c, buf, &h, &mh, &len); mh->msg = msgtype; x=0; codeset = 0; while(ies[x] > -1) { res = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); if (res < 0) { pri_error(pri, "!! Unable to add IE '%s'\n", ie2str(ies[x])); return -1; } offset += res; len -= res; x++; } /* Invert the logic */ len = sizeof(buf) - len; q931_xmit(pri, h, len, 1); c->acked = 1; return 0; } static int status_ies[] = { Q931_CAUSE, Q931_CALL_STATE, -1 }; static int q931_status(struct pri *pri, q931_call *c, int cause) { q931_call *cur = NULL; if (!cause) cause = PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY; if (c->cr > -1) cur = *pri->callpool; while(cur) { if (cur->cr == c->cr) { cur->cause=cause; cur->causecode = CODE_CCITT; cur->causeloc = LOC_USER; break; } cur = cur->next; } if (!cur) { pri_message(pri, "YYY Here we get reset YYY\n"); /* something went wrong, respond with "no such call" */ c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; cur=c; } return send_message(pri, cur, Q931_STATUS, status_ies); } static int information_ies[] = { Q931_IE_KEYPAD_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; int q931_information(struct pri *pri, q931_call *c, char digit) { c->callednum[0] = digit; c->callednum[1] = '\0'; return send_message(pri, c, Q931_INFORMATION, information_ies); } static int keypad_facility_ies[] = { Q931_IE_KEYPAD_FACILITY, -1 }; int q931_keypad_facility(struct pri *pri, q931_call *call, char *digits) { libpri_copy_string(call->keypad_digits, digits, sizeof(call->keypad_digits)); return send_message(pri, call, Q931_INFORMATION, keypad_facility_ies); } static int restart_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; static int restart_ack(struct pri *pri, q931_call *c) { UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; return send_message(pri, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies); } static int facility_ies[] = { Q931_IE_FACILITY, -1 }; int q931_facility(struct pri*pri, q931_call *c) { return send_message(pri, c, Q931_FACILITY, facility_ies); } static int notify_ies[] = { Q931_IE_NOTIFY_IND, -1 }; int q931_notify(struct pri *pri, q931_call *c, int channel, int info) { if ((pri->switchtype == PRI_SWITCH_EUROISDN_T1) || (pri->switchtype != PRI_SWITCH_EUROISDN_E1)) { if ((info > 0x2) || (info < 0x00)) return 0; } if (info >= 0) c->notify = info & 0x7F; else c->notify = -1; return send_message(pri, c, Q931_NOTIFY, notify_ies); } #ifdef ALERTING_NO_PROGRESS static int call_progress_ies[] = { -1 }; #else static int call_progress_ies[] = { Q931_PROGRESS_INDICATOR, -1 }; #endif int q931_call_progress(struct pri *pri, q931_call *c, int channel, int info) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; channel &= 0xff; c->channelno = channel; } if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else { /* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */ pri_error(pri, "XXX Progress message requested but no information is provided\n"); c->progressmask = 0; } c->alive = 1; return send_message(pri, c, Q931_PROGRESS, call_progress_ies); } #ifdef ALERTING_NO_PROGRESS static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, -1 }; #else static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 }; #endif int q931_call_proceeding(struct pri *pri, q931_call *c, int channel, int info) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; channel &= 0xff; c->channelno = channel; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_INCOMING_CALL_PROCEEDING); c->peercallstate = Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING; if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else c->progressmask = 0; c->proc = 1; c->alive = 1; return send_message(pri, c, Q931_CALL_PROCEEDING, call_proceeding_ies); } #ifndef ALERTING_NO_PROGRESS static int alerting_ies[] = { Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, -1 }; #else static int alerting_ies[] = { -1 }; #endif int q931_alerting(struct pri *pri, q931_call *c, int channel, int info) { if (!c->proc) q931_call_proceeding(pri, c, channel, 0); if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else c->progressmask = 0; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_RECEIVED); c->peercallstate = Q931_CALL_STATE_CALL_DELIVERED; c->alive = 1; return send_message(pri, c, Q931_ALERTING, alerting_ies); } static int connect_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 }; int q931_setup_ack(struct pri *pri, q931_call *c, int channel, int nonisdn) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; channel &= 0xff; c->channelno = channel; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; if (nonisdn && (pri->switchtype != PRI_SWITCH_DMS100)) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_CALLED_NOT_ISDN; } else c->progressmask = 0; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_OVERLAP_RECEIVING); c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING; c->alive = 1; return send_message(pri, c, Q931_SETUP_ACKNOWLEDGE, connect_ies); } static void pri_connect_timeout(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "Timed out looking for connect acknowledge\n"); q931_disconnect(pri, c, PRI_CAUSE_NORMAL_CLEARING); } static void pri_release_timeout(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "Timed out looking for release complete\n"); c->t308_timedout++; c->alive = 1; q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); } static void pri_release_finaltimeout(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; c->alive = 1; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "Final time-out looking for release complete\n"); c->t308_timedout++; c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; pri->schedev = 1; pri->ev.e = PRI_EVENT_HANGUP_ACK; pri->ev.hangup.channel = c->channelno; pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); q931_hangup(pri, c, c->cause); } static void pri_disconnect_timeout(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "Timed out looking for release\n"); c->alive = 1; q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); } int q931_connect(struct pri *pri, q931_call *c, int channel, int nonisdn) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; channel &= 0xff; c->channelno = channel; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; if (nonisdn && (pri->switchtype != PRI_SWITCH_DMS100)) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_CALLED_NOT_ISDN; } else c->progressmask = 0; if(pri->localtype == PRI_NETWORK || pri->switchtype == PRI_SWITCH_QSIG) UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); else UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_ACTIVE; c->alive = 1; /* Connect request timer */ if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; if ((c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && (!pri->subchannel)) c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T313], pri_connect_timeout, c); return send_message(pri, c, Q931_CONNECT, connect_ies); } static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; int q931_release(struct pri *pri, q931_call *c, int cause) { UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_RELEASE_REQUEST); /* c->peercallstate stays the same */ if (c->alive) { c->alive = 0; c->cause = cause; c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; if (c->acked) { if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); if (!c->t308_timedout) { c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_timeout, c); } else { c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_finaltimeout, c); } return send_message(pri, c, Q931_RELEASE, release_ies); } else return send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */ } else return 0; } static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; int q931_restart(struct pri *pri, int channel) { struct q931_call *c; c = q931_getcall(pri, 0 | 0x8000); if (!c) return -1; if (!channel) return -1; c->ri = 0; c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; channel &= 0xff; c->channelno = channel; c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_RESTART); c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; return send_message(pri, c, Q931_RESTART, restart_ies); } static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; int q931_disconnect(struct pri *pri, q931_call *c, int cause) { UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_DISCONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_DISCONNECT_INDICATION; if (c->alive) { c->alive = 0; c->cause = cause; c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; c->sendhangupack = 1; if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T305], pri_disconnect_timeout, c); return send_message(pri, c, Q931_DISCONNECT, disconnect_ies); } else return 0; } static int setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_NETWORK_SPEC_FAC, Q931_DISPLAY, Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_REDIRECTING_NUMBER, Q931_IE_USER_USER, Q931_SENDING_COMPLETE, Q931_IE_ORIGINATING_LINE_INFO, Q931_IE_GENERIC_DIGITS, -1 }; static int gr303_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, -1 }; static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req) { int res; c->transcapability = req->transmode; c->transmoderate = TRANS_MODE_64_CIRCUIT; if (!req->userl1) req->userl1 = PRI_LAYER_1_ULAW; c->userl1 = req->userl1; c->ds1no = (req->channel & 0xff00) >> 8; c->ds1explicit = (req->channel & 0x10000) >> 16; req->channel &= 0xff; if ((pri->localtype == PRI_CPE) && pri->subchannel) { req->channel = 0; req->exclusive = 0; } c->channelno = req->channel; c->slotmap = -1; c->nonisdn = req->nonisdn; c->newcall = 0; c->justsignalling = req->justsignalling; c->complete = req->numcomplete; if (req->exclusive) c->chanflags = FLAG_EXCLUSIVE; else if (c->channelno) c->chanflags = FLAG_PREFERRED; if (req->caller) { libpri_copy_string(c->callernum, req->caller, sizeof(c->callernum)); c->callerplan = req->callerplan; if (req->callername) libpri_copy_string(c->callername, req->callername, sizeof(c->callername)); else c->callername[0] = '\0'; if ((pri->switchtype == PRI_SWITCH_DMS100) || (pri->switchtype == PRI_SWITCH_ATT4ESS)) { /* Doesn't like certain presentation types */ if (!(req->callerpres & 0x7c)) req->callerpres = PRES_ALLOWED_NETWORK_NUMBER; } c->callerpres = req->callerpres; } else { c->callernum[0] = '\0'; c->callername[0] = '\0'; c->callerplan = PRI_UNKNOWN; c->callerpres = PRES_NUMBER_NOT_AVAILABLE; } if (req->redirectingnum) { libpri_copy_string(c->redirectingnum, req->redirectingnum, sizeof(c->redirectingnum)); c->redirectingplan = req->redirectingplan; if ((pri->switchtype == PRI_SWITCH_DMS100) || (pri->switchtype == PRI_SWITCH_ATT4ESS)) { /* Doesn't like certain presentation types */ if (!(req->redirectingpres & 0x7c)) req->redirectingpres = PRES_ALLOWED_NETWORK_NUMBER; } c->redirectingpres = req->redirectingpres; c->redirectingreason = req->redirectingreason; } else { c->redirectingnum[0] = '\0'; c->redirectingplan = PRI_UNKNOWN; c->redirectingpres = PRES_NUMBER_NOT_AVAILABLE; c->redirectingreason = PRI_REDIR_UNKNOWN; } if (req->called) { libpri_copy_string(c->callednum, req->called, sizeof(c->callednum)); c->calledplan = req->calledplan; } else return -1; if (req->useruserinfo) libpri_copy_string(c->useruserinfo, req->useruserinfo, sizeof(c->useruserinfo)); else c->useruserinfo[0] = '\0'; if (req->nonisdn && (pri->switchtype == PRI_SWITCH_NI2)) c->progressmask = PRI_PROG_CALLER_NOT_ISDN; else c->progressmask = 0; pri_call_add_standard_apdus(pri, c); if (pri->subchannel) res = send_message(pri, c, Q931_SETUP, gr303_setup_ies); else if (c->justsignalling) res = send_message(pri, c, Q931_SETUP, cis_setup_ies); else res = send_message(pri, c, Q931_SETUP, setup_ies); if (!res) { c->alive = 1; /* make sure we call PRI_EVENT_HANGUP_ACK once we send/receive RELEASE_COMPLETE */ c->sendhangupack = 1; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_INITIATED); c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING; } return res; } static int release_complete_ies[] = { Q931_IE_USER_USER, -1 }; static int q931_release_complete(struct pri *pri, q931_call *c, int cause) { int res = 0; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (cause > -1) { c->cause = cause; c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; /* release_ies has CAUSE in it */ res = send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies); } else res = send_message(pri, c, Q931_RELEASE_COMPLETE, release_complete_ies); c->alive = 0; /* release the structure */ res += q931_hangup(pri,c,cause); return res; } static int connect_acknowledge_ies[] = { -1 }; static int gr303_connect_acknowledge_ies[] = { Q931_CHANNEL_IDENT, -1 }; static int q931_connect_acknowledge(struct pri *pri, q931_call *c) { if (pri->subchannel) { if (pri->localtype == PRI_CPE) return send_message(pri, c, Q931_CONNECT_ACKNOWLEDGE, gr303_connect_acknowledge_ies); } else return send_message(pri, c, Q931_CONNECT_ACKNOWLEDGE, connect_acknowledge_ies); return 0; } int q931_hangup(struct pri *pri, q931_call *c, int cause) { int disconnect = 1; int release_compl = 0; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate)); if (!pri || !c) return -1; /* If mandatory IE was missing, insist upon that cause code */ if (c->cause == PRI_CAUSE_MANDATORY_IE_MISSING) cause = c->cause; if (cause == 34 || cause == 44 || cause == 82 || cause == 1 || cause == 81) { /* We'll send RELEASE_COMPLETE with these causes */ disconnect = 0; release_compl = 1; } if (cause == 6 || cause == 7 || cause == 26) { /* We'll send RELEASE with these causes */ disconnect = 0; } /* All other causes we send with DISCONNECT */ switch(c->ourcallstate) { case Q931_CALL_STATE_NULL: if (c->peercallstate == Q931_CALL_STATE_NULL) /* free the resources if we receive or send REL_COMPL */ q931_destroycall(pri, c->cr); else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST) q931_release_complete(pri,c,cause); break; case Q931_CALL_STATE_CALL_INITIATED: /* we sent SETUP */ case Q931_CALL_STATE_OVERLAP_SENDING: /* received SETUP_ACKNOWLEDGE */ case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: /* received CALL_PROCEEDING */ case Q931_CALL_STATE_CALL_DELIVERED: /* received ALERTING */ case Q931_CALL_STATE_CALL_PRESENT: /* received SETUP */ case Q931_CALL_STATE_CALL_RECEIVED: /* sent ALERTING */ case Q931_CALL_STATE_CONNECT_REQUEST: /* sent CONNECT */ case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: /* we sent CALL_PROCEEDING */ case Q931_CALL_STATE_OVERLAP_RECEIVING: /* received SETUP_ACKNOWLEDGE */ /* send DISCONNECT in general */ if (c->peercallstate != Q931_CALL_STATE_NULL && c->peercallstate != Q931_CALL_STATE_DISCONNECT_REQUEST && c->peercallstate != Q931_CALL_STATE_DISCONNECT_INDICATION && c->peercallstate != Q931_CALL_STATE_RELEASE_REQUEST && c->peercallstate != Q931_CALL_STATE_RESTART_REQUEST && c->peercallstate != Q931_CALL_STATE_RESTART) { if (disconnect) q931_disconnect(pri,c,cause); else if (release_compl) q931_release_complete(pri,c,cause); else q931_release(pri,c,cause); } else pri_error(pri, "Wierd, doing nothing but this shouldn't happen, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate)); break; case Q931_CALL_STATE_ACTIVE: /* received CONNECT */ q931_disconnect(pri,c,cause); break; case Q931_CALL_STATE_DISCONNECT_REQUEST: /* sent DISCONNECT */ q931_release(pri,c,cause); break; case Q931_CALL_STATE_DISCONNECT_INDICATION: /* received DISCONNECT */ if (c->peercallstate == Q931_CALL_STATE_DISCONNECT_REQUEST) { c->alive = 1; q931_release(pri,c,cause); } break; case Q931_CALL_STATE_RELEASE_REQUEST: /* sent RELEASE */ /* don't do anything, waiting for RELEASE_COMPLETE */ break; case Q931_CALL_STATE_RESTART: case Q931_CALL_STATE_RESTART_REQUEST: /* sent RESTART */ pri_error(pri, "q931_hangup shouldn't be called in this state, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate)); break; default: pri_error(pri, "We're not yet handling hanging up when our state is %d, contact support@digium.com, ourstate %s, peerstate %s\n", c->ourcallstate, callstate2str(c->ourcallstate), callstate2str(c->peercallstate)); return -1; } /* we did handle hangup properly at this point */ return 0; } int q931_receive(struct pri *pri, q931_h *h, int len) { q931_mh *mh; q931_call *c; q931_ie *ie; unsigned int x; int y; int res; int r; int mandies[MAX_MAND_IES]; int missingmand; int codeset, cur_codeset; int last_ie[8]; struct apdu_event *cur = NULL; memset(last_ie, 0, sizeof(last_ie)); if (pri->debug & PRI_DEBUG_Q931_DUMP) q931_dump(pri, h, len, 0); #ifdef LIBPRI_COUNTERS pri->q931_rxcount++; #endif mh = (q931_mh *)(h->contents + h->crlen); if ((h->pd == 0x3) || (h->pd == 0x43)) { /* This is the weird maintenance stuff. We majorly KLUDGE this by changing byte 4 from a 0xf (SERVICE) to a 0x7 (SERVICE ACKNOWLEDGE) */ h->raw[h->crlen + 2] -= 0x8; q931_xmit(pri, h, len, 1); return 0; } else if (h->pd != pri->protodisc) { pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); return 0; } c = q931_getcall(pri, q931_cr(h)); if (!c) { pri_error(pri, "Unable to locate call %d\n", q931_cr(h)); return -1; } /* Preliminary handling */ switch(mh->msg) { case Q931_RESTART: if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "-- Processing Q.931 Restart\n"); /* Reset information */ c->channelno = -1; c->slotmap = -1; c->chanflags = 0; c->ds1no = 0; c->ri = -1; break; case Q931_FACILITY: c->callername[0] = '\0'; break; case Q931_SETUP: if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, "-- Processing Q.931 Call Setup\n"); c->channelno = -1; c->slotmap = -1; c->chanflags = 0; c->ds1no = 0; c->ri = -1; c->transcapability = -1; c->transmoderate = -1; c->transmultiple = -1; c->userl1 = -1; c->userl2 = -1; c->userl3 = -1; c->rateadaption = -1; c->calledplan = -1; c->callerplan = -1; c->callerpres = -1; c->callernum[0] = '\0'; c->callednum[0] = '\0'; c->callername[0] = '\0'; c->callerani[0] = '\0'; c->callerplanani = -1; c->redirectingplan = -1; c->redirectingpres = -1; c->redirectingreason = -1; c->origcalledplan = -1; c->origcalledpres = -1; c->origredirectingreason = -1; c->redirectingnum[0] = '\0'; c->origcallednum[0] = '\0'; c->redirectingname[0] = '\0'; c->origcalledname[0] = '\0'; c->useruserprotocoldisc = -1; c->useruserinfo[0] = '\0'; c->complete = 0; c->nonisdn = 0; c->aoc_units = -1; /* Fall through */ case Q931_CONNECT: case Q931_ALERTING: case Q931_PROGRESS: c->useruserinfo[0] = '\0'; c->cause = -1; /* Fall through */ case Q931_CALL_PROCEEDING: c->progress = -1; c->progressmask = 0; break; case Q931_CONNECT_ACKNOWLEDGE: if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; break; case Q931_RELEASE: case Q931_DISCONNECT: c->cause = -1; c->causecode = -1; c->causeloc = -1; c->aoc_units = -1; if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; break; case Q931_RELEASE_COMPLETE: if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; /* Fall through */ case Q931_STATUS: c->cause = -1; c->causecode = -1; c->causeloc = -1; c->sugcallstate = -1; c->aoc_units = -1; break; case Q931_RESTART_ACKNOWLEDGE: c->channelno = -1; break; case Q931_INFORMATION: c->callednum[0] = '\0'; break; case Q931_STATUS_ENQUIRY: break; case Q931_SETUP_ACKNOWLEDGE: break; case Q931_NOTIFY: break; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: case Q931_HOLD: case Q931_HOLD_ACKNOWLEDGE: case Q931_HOLD_REJECT: case Q931_RETRIEVE: case Q931_RETRIEVE_ACKNOWLEDGE: case Q931_RETRIEVE_REJECT: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: case Q931_SUSPEND: case Q931_SUSPEND_ACKNOWLEDGE: case Q931_SUSPEND_REJECT: pri_error(pri, "!! Not yet handling pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: pri_error(pri, "!! Don't know how to pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); q931_status(pri,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) q931_destroycall(pri,c->cr); return -1; } /* Handle IEs */ memset(mandies, 0, sizeof(mandies)); missingmand = 0; for (x=0;xmsg) { memcpy(mandies, msgs[x].mandies, sizeof(mandies)); } } x = 0; /* Do real IE processing */ len -= (h->crlen + 3); codeset = cur_codeset = 0; while(len) { ie = (q931_ie *)(mh->data + x); for (y=0;yie)) mandies[y] = 0; } r = ielen(ie); if (r > len) { pri_error(pri, "XXX Message longer than it should be?? XXX\n"); return -1; } /* Special processing for codeset shifts */ switch (ie->ie & 0xf8) { case Q931_LOCKING_SHIFT: y = ie->ie & 7; /* Requested codeset */ /* Locking shifts couldn't go to lower codeset, and couldn't follows non-locking shifts - verify this */ if ((cur_codeset != codeset) && (pri->debug & PRI_DEBUG_Q931_ANOMALY)) pri_message(pri, "XXX Locking shift immediately follows non-locking shift (from %d through %d to %d) XXX\n", codeset, cur_codeset, y); if (y > 0) { if ((y < codeset) && (pri->debug & PRI_DEBUG_Q931_ANOMALY)) pri_error(pri, "!! Trying to locked downshift codeset from %d to %d !!\n", codeset, y); codeset = cur_codeset = y; } else { /* Locking shift to codeset 0 is forbidden by all specifications */ pri_error(pri, "!! Invalid locking shift to codeset 0 !!\n"); } break; case Q931_NON_LOCKING_SHIFT: cur_codeset = ie->ie & 7; break; default: /* Sanity check for IE code order */ if (!(ie->ie & 0x80)) { if (last_ie[cur_codeset] > ie->ie) { if ((pri->debug & PRI_DEBUG_Q931_ANOMALY)) pri_message(pri, "XXX Out-of-order IE %d at codeset %d (last was %d)\n", ie->ie, cur_codeset, last_ie[cur_codeset]); } else last_ie[cur_codeset] = ie->ie; } /* Ignore non-locking shifts for TR41459-based signalling */ switch (pri->switchtype) { case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_ATT4ESS: if (cur_codeset != codeset) { if ((pri->debug & PRI_DEBUG_Q931_DUMP)) pri_message(pri, "XXX Ignoring IE %d for temporary codeset %d XXX\n", ie->ie, cur_codeset); break; } /* Fall through */ default: y = q931_handle_ie(cur_codeset, pri, c, mh->msg, ie); /* XXX Applicable to codeset 0 only? XXX */ if (!cur_codeset && !(ie->ie & 0xf0) && (y < 0)) mandies[MAX_MAND_IES - 1] = Q931_FULL_IE(cur_codeset, ie->ie); } /* Reset current codeset */ cur_codeset = codeset; } x += r; len -= r; } missingmand = 0; for (x=0;x that's not an error */ if (((pri->localtype != PRI_NETWORK) || (mh->msg != Q931_SETUP) || (mandies[x] != Q931_CHANNEL_IDENT)) && ((mh->msg != Q931_PROGRESS) || (mandies[x] != Q931_PROGRESS_INDICATOR))) { pri_error(pri, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x])); missingmand++; } } } /* Post handling */ switch(mh->msg) { case Q931_RESTART: if (missingmand) { q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); q931_destroycall(pri, c->cr); break; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_RESTART); c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; /* Send back the Restart Acknowledge */ restart_ack(pri, c); /* Notify user of restart event */ pri->ev.e = PRI_EVENT_RESTART; pri->ev.restart.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); return Q931_RES_HAVEEVENT; case Q931_SETUP: if (missingmand) { q931_release_complete(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); break; } /* Must be new call */ if (!c->newcall) { break; } if (c->progressmask & PRI_PROG_CALLER_NOT_ISDN) c->nonisdn = 1; c->newcall = 0; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_PRESENT); c->peercallstate = Q931_CALL_STATE_CALL_INITIATED; /* it's not yet a call since higher level can respond with RELEASE or RELEASE_COMPLETE */ c->alive = 0; if (c->transmoderate != TRANS_MODE_64_CIRCUIT) { q931_release_complete(pri, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL); break; } pri->ev.e = PRI_EVENT_RING; pri->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.ring.callingpres = c->callerpres; pri->ev.ring.callingplan = c->callerplan; pri->ev.ring.callingplanani = c->callerplanani; pri->ev.ring.callingplanrdnis = c->redirectingplan; pri->ev.ring.callingplanorigcalled = c->origcalledplan; pri->ev.ring.ani2 = c->ani2; libpri_copy_string(pri->ev.ring.callingani, c->callerani, sizeof(pri->ev.ring.callingani)); libpri_copy_string(pri->ev.ring.callingnum, c->callernum, sizeof(pri->ev.ring.callingnum)); libpri_copy_string(pri->ev.ring.callingname, c->callername, sizeof(pri->ev.ring.callingname)); pri->ev.ring.calledplan = c->calledplan; libpri_copy_string(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr)); libpri_copy_string(pri->ev.ring.callednum, c->callednum, sizeof(pri->ev.ring.callednum)); libpri_copy_string(pri->ev.ring.origcalledname, c->origcalledname, sizeof(pri->ev.ring.origcalledname)); libpri_copy_string(pri->ev.ring.origcallednum, c->origcallednum, sizeof(pri->ev.ring.origcallednum)); libpri_copy_string(pri->ev.ring.redirectingnum, c->redirectingnum, sizeof(pri->ev.ring.redirectingnum)); libpri_copy_string(pri->ev.ring.redirectingname, c->redirectingname, sizeof(pri->ev.ring.redirectingname)); libpri_copy_string(pri->ev.ring.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo)); c->useruserinfo[0] = '\0'; pri->ev.ring.redirectingreason = c->redirectingreason; pri->ev.ring.origredirectingreason = c->origredirectingreason; pri->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE); pri->ev.ring.cref = c->cr; pri->ev.ring.call = c; pri->ev.ring.layer1 = c->userl1; pri->ev.ring.complete = c->complete; pri->ev.ring.ctype = c->transcapability; pri->ev.ring.redirectingreason = c->redirectingreason; pri->ev.ring.progress = c->progress; pri->ev.ring.progressmask = c->progressmask; return Q931_RES_HAVEEVENT; case Q931_ALERTING: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_DELIVERED); c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED; pri->ev.e = PRI_EVENT_RINGING; pri->ev.ringing.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.ringing.cref = c->cr; pri->ev.ringing.call = c; pri->ev.ringing.progress = c->progress; pri->ev.ringing.progressmask = c->progressmask; libpri_copy_string(pri->ev.ringing.useruserinfo, c->useruserinfo, sizeof(pri->ev.ringing.useruserinfo)); c->useruserinfo[0] = '\0'; cur = c->apdus; while (cur) { if (!cur->sent && cur->message == Q931_FACILITY) { q931_facility(pri, c); break; } cur = cur->next; } return Q931_RES_HAVEEVENT; case Q931_CONNECT: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if (c->ourcallstate == Q931_CALL_STATE_ACTIVE) { q931_status(pri, c, PRI_CAUSE_WRONG_MESSAGE); break; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST; pri->ev.e = PRI_EVENT_ANSWER; pri->ev.answer.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.answer.cref = c->cr; pri->ev.answer.call = c; pri->ev.answer.progress = c->progress; pri->ev.answer.progressmask = c->progressmask; libpri_copy_string(pri->ev.answer.useruserinfo, c->useruserinfo, sizeof(pri->ev.answer.useruserinfo)); c->useruserinfo[0] = '\0'; q931_connect_acknowledge(pri, c); if (c->justsignalling) { /* Make sure WE release when we initiatie a signalling only connection */ q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); break; } else return Q931_RES_HAVEEVENT; case Q931_FACILITY: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } pri->ev.e = PRI_EVENT_FACNAME; libpri_copy_string(pri->ev.facname.callingname, c->callername, sizeof(pri->ev.facname.callingname)); libpri_copy_string(pri->ev.facname.callingnum, c->callernum, sizeof(pri->ev.facname.callingnum)); pri->ev.facname.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.facname.cref = c->cr; pri->ev.facname.call = c; #if 0 pri_message(pri, "Sending facility event (%s/%s)\n", pri->ev.facname.callingname, pri->ev.facname.callingnum); #endif return Q931_RES_HAVEEVENT; case Q931_PROGRESS: if (missingmand) { q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); q931_destroycall(pri, c->cr); break; } pri->ev.e = PRI_EVENT_PROGRESS; pri->ev.proceeding.cause = c->cause; /* Fall through */ case Q931_CALL_PROCEEDING: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if ((c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) && (c->ourcallstate != Q931_CALL_STATE_OVERLAP_SENDING) && (c->ourcallstate != Q931_CALL_STATE_CALL_DELIVERED) && (c->ourcallstate != Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING)) { q931_status(pri,c,PRI_CAUSE_WRONG_MESSAGE); break; } pri->ev.proceeding.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); if (mh->msg == Q931_CALL_PROCEEDING) { pri->ev.e = PRI_EVENT_PROCEEDING; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); c->peercallstate = Q931_CALL_STATE_INCOMING_CALL_PROCEEDING; } pri->ev.proceeding.progress = c->progress; pri->ev.proceeding.progressmask = c->progressmask; pri->ev.proceeding.cref = c->cr; pri->ev.proceeding.call = c; cur = c->apdus; while (cur) { if (!cur->sent && cur->message == Q931_FACILITY) { q931_facility(pri, c); break; } cur = cur->next; } return Q931_RES_HAVEEVENT; case Q931_CONNECT_ACKNOWLEDGE: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if (!(c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && !(c->ourcallstate == Q931_CALL_STATE_ACTIVE && (pri->localtype == PRI_NETWORK || pri->switchtype == PRI_SWITCH_QSIG))) { q931_status(pri,c,PRI_CAUSE_WRONG_MESSAGE); break; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_ACTIVE; break; case Q931_STATUS: if (missingmand) { q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); q931_destroycall(pri, c->cr); break; } if (c->newcall) { if (c->cr & 0x7fff) q931_release_complete(pri,c,PRI_CAUSE_WRONG_CALL_STATE); break; } /* Do nothing */ /* Also when the STATUS asks for the call of an unexisting reference send RELEASE_COMPL */ if ((pri->debug & PRI_DEBUG_Q931_ANOMALY) && (c->cause != PRI_CAUSE_INTERWORKING)) pri_error(pri, "Received unsolicited status: %s\n", pri_cause2str(c->cause)); /* Workaround for S-12 ver 7.3 - it responds for invalid/non-implemented IEs at SETUP with null call state */ if (!c->sugcallstate && (c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED)) { pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); /* Free resources */ UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (c->alive) { pri->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; c->alive = 0; } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; pri->ev.e = PRI_EVENT_HANGUP_ACK; q931_hangup(pri, c, c->cause); } else { q931_hangup(pri, c, c->cause); res = 0; } if (res) return res; } break; case Q931_RELEASE_COMPLETE: UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Free resources */ if (c->alive) { pri->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; c->alive = 0; } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; pri->ev.e = PRI_EVENT_HANGUP_ACK; pri_hangup(pri, c, c->cause); } else res = 0; if (res) return res; else q931_hangup(pri,c,c->cause); break; case Q931_RELEASE: if (missingmand) { /* Force cause to be mandatory IE missing */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } if (c->ourcallstate == Q931_CALL_STATE_RELEASE_REQUEST) c->peercallstate = Q931_CALL_STATE_NULL; else { c->peercallstate = Q931_CALL_STATE_RELEASE_REQUEST; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); pri->ev.e = PRI_EVENT_HANGUP; pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Don't send release complete if they send us release while we sent it, assume a NULL state */ if (c->newcall) q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); else return Q931_RES_HAVEEVENT; break; case Q931_DISCONNECT: if (missingmand) { /* Still let user call release */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_DISCONNECT_INDICATION); c->peercallstate = Q931_CALL_STATE_DISCONNECT_REQUEST; c->sendhangupack = 1; /* Return such an event */ pri->ev.e = PRI_EVENT_HANGUP_REQ; pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; if (c->alive) return Q931_RES_HAVEEVENT; else q931_hangup(pri,c,c->cause); break; case Q931_RESTART_ACKNOWLEDGE: UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; pri->ev.e = PRI_EVENT_RESTART_ACK; pri->ev.restartack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); return Q931_RES_HAVEEVENT; case Q931_INFORMATION: /* XXX We're handling only INFORMATION messages that contain overlap dialing received digit + the "Complete" msg which is basically an EOF on further digits XXX */ if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { pri->ev.e = PRI_EVENT_KEYPAD_DIGIT; pri->ev.digit.call = c; pri->ev.digit.channel = c->channelno | (c->ds1no << 8); libpri_copy_string(pri->ev.digit.digits, c->keypad_digits, sizeof(pri->ev.digit.digits)); /* Make sure we clear it out before we return */ c->keypad_digits[0] = '\0'; return Q931_RES_HAVEEVENT; } pri->ev.e = PRI_EVENT_INFO_RECEIVED; pri->ev.ring.call = c; pri->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); libpri_copy_string(pri->ev.ring.callednum, c->callednum, sizeof(pri->ev.ring.callednum)); libpri_copy_string(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr)); pri->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */ return Q931_RES_HAVEEVENT; case Q931_STATUS_ENQUIRY: if (c->newcall) { q931_release_complete(pri, c, PRI_CAUSE_INVALID_CALL_REFERENCE); } else q931_status(pri,c, 0); break; case Q931_SETUP_ACKNOWLEDGE: if (c->newcall) { q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_OVERLAP_SENDING); c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING; pri->ev.e = PRI_EVENT_SETUP_ACK; pri->ev.setup_ack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.setup_ack.call = c; cur = c->apdus; while (cur) { if (!cur->sent && cur->message == Q931_FACILITY) { q931_facility(pri, c); break; } cur = cur->next; } return Q931_RES_HAVEEVENT; case Q931_NOTIFY: pri->ev.e = PRI_EVENT_NOTIFY; pri->ev.notify.channel = c->channelno; pri->ev.notify.info = c->notify; return Q931_RES_HAVEEVENT; case Q931_USER_INFORMATION: case Q931_SEGMENT: case Q931_CONGESTION_CONTROL: case Q931_HOLD: case Q931_HOLD_ACKNOWLEDGE: case Q931_HOLD_REJECT: case Q931_RETRIEVE: case Q931_RETRIEVE_ACKNOWLEDGE: case Q931_RETRIEVE_REJECT: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: case Q931_SUSPEND: case Q931_SUSPEND_ACKNOWLEDGE: case Q931_SUSPEND_REJECT: pri_error(pri, "!! Not yet handling post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: pri_error(pri, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); q931_status(pri,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) q931_destroycall(pri,c->cr); return -1; } return 0; } /* Clear a call, although we did not receive any hangup notification. */ static int pri_internal_clear(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; int res; if (c->retranstimer) pri_schedule_del(pri, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; c->cause = -1; c->causecode = -1; c->causeloc = -1; c->sugcallstate = -1; c->aoc_units = -1; UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); pri->ev.hangup.cause = c->cause; pri->ev.hangup.cref = c->cr; pri->ev.hangup.call = c; pri->ev.hangup.aoc_units = c->aoc_units; libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); /* Free resources */ if (c->alive) { pri->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; c->alive = 0; } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; pri->ev.e = PRI_EVENT_HANGUP_ACK; q931_hangup(pri, c, c->cause); } else { res = 0; q931_hangup(pri, c, c->cause); } return res; } /* Handle T309 timeout for an active call. */ static void pri_dl_down_timeout(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, DBGHEAD "Timed out waiting for data link re-establishment\n", DBGINFO); c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) pri->schedev = 1; } /* Handle Layer 2 down event for a non active call. */ static void pri_dl_down_cancelcall(void *data) { struct q931_call *c = data; struct pri *pri = c->pri; if (pri->debug & PRI_DEBUG_Q931_STATE) pri_message(pri, DBGHEAD "Cancel non active call after data link failure\n", DBGINFO); c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) pri->schedev = 1; } /* Receive an indication from Layer 2 */ void q931_dl_indication(struct pri *pri, int event) { q931_call *cur = NULL; /* Just return if T309 is not enabled. */ if (!pri || pri->timers[PRI_TIMER_T309] < 0) return; switch (event) { case PRI_EVENT_DCHAN_DOWN: pri_message(pri, DBGHEAD "link is DOWN\n", DBGINFO); cur = *pri->callpool; while(cur) { if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE) { /* For a call in Active state, activate T309 only if there is no timer already running. */ if (!cur->retranstimer) { pri_message(pri, DBGHEAD "activate T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno); cur->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T309], pri_dl_down_timeout, cur); } } else if (cur->ourcallstate != Q931_CALL_STATE_NULL) { /* For a call that is not in Active state, schedule internal clearing of the call 'ASAP' (delay 0). */ pri_message(pri, DBGHEAD "cancel call %d on channel %d in state %d (%s)\n", DBGINFO, cur->cr, cur->channelno, cur->ourcallstate, callstate2str(cur->ourcallstate)); if (cur->retranstimer) pri_schedule_del(pri, cur->retranstimer); cur->retranstimer = pri_schedule_event(pri, 0, pri_dl_down_cancelcall, cur); } cur = cur->next; } break; case PRI_EVENT_DCHAN_UP: pri_message(pri, DBGHEAD "link is UP\n", DBGINFO); cur = *pri->callpool; while(cur) { if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE && cur->retranstimer) { pri_message(pri, DBGHEAD "cancel T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno); pri_schedule_del(pri, cur->retranstimer); cur->retranstimer = 0; q931_status(pri, cur, PRI_CAUSE_NORMAL_UNSPECIFIED); } else if (cur->ourcallstate != Q931_CALL_STATE_NULL && cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_REQUEST && cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_INDICATION && cur->ourcallstate != Q931_CALL_STATE_RELEASE_REQUEST) { /* The STATUS message sent here is not required by Q.931, but it may help anyway. */ q931_status(pri, cur, PRI_CAUSE_NORMAL_UNSPECIFIED); } cur = cur->next; } break; default: pri_message(pri, DBGHEAD "unexpected event %d.\n", DBGINFO, event); } } int q931_call_getcrv(struct pri *pri, q931_call *call, int *callmode) { if (callmode) *callmode = call->cr & 0x7; return ((call->cr & 0x7fff) >> 3); } int q931_call_setcrv(struct pri *pri, q931_call *call, int crv, int callmode) { call->cr = (crv << 3) & 0x7fff; call->cr |= (callmode & 0x7); return 0; }