Asterisk PBX



  • @dkf said in Asterisk PBX:

    @onyx said in Asterisk PBX:

    T-Scum

    It could be worse. It could be Vodafuck.

    There's also O2Offline.


  • BINNED

    Hokay, since I had to poke around this particular hornet's nest again due to "who the fuck even approved this?" client request I remembered how stupid it is. Rant time!

    First of all, I have to point out that Asterisk was not designed as a SIP PBX specifically. It was instead designed as a PBX that can (with additional hardware, where needed) handle everything from POTS and ISDN to craziness like H323, MGCP, SCCP and every other way to transfer voice over a wire in an overly complicated way you can think of. SIP support is just one module out of many that you can use.

    For years, the only way to connect SIP to Asterisk was its, in-house developed, chan_sip module. It worked, but just barely. Not that it crashed or anything, but it didn't support the entirety of SIP either (hell, rich presence doesn't work at all to this day without extra fuckery). One of the most glaring omissions was an ability to use multiple devices on a single endpoint (think having the same phone number on your desk phone, a SIP client on your PC, and you also connect your mobile phone to it). Enter PJSIP, an open source library which is much more flexible (and is probably used in most SIP clients these days) which someone wrapped into an Asterisk module. Modern features, yay!

    Now, since Asterisk is an "universal" PBX, it's not well equipped for something like this. To make the problem clear, let's go with an example. Let's say you have an extension whose phone number is 100. To make a call to it you'd do this in your dialplan:

    Dial(technology/100,more,options,here) - where technology is basically the protocol you're using, so for POTS and ISDN it would be DAHDI/100, for old chan_sip it would be SIP/100. The other options are things like how long to ring the phone or a piece of code to execute when the phone is picked up etc. So, with PJSIP, in its simplest form, you'd write Dial(PSJIP/100). The phone rings, everyone's happy. Yay.

    Now you want to connect your mobile phone to PBX (either using a third party SIP client, or, for example, Android's built-in SIP client). You set PJSIP up to allow multiple contacts per endpoint, you can make calls from both of them, cool. But here's the problem: calling both of those phones. See, Asterisk has no concept of multiple contacts per endpoint. Dial cannot cope. If you try to just do it naively Asterisk will just call whichever contact registered last. But, fear not, there's a solution! You can parallel dial in Asterisk. Basically, you just list multiple contacts to call and separate them with an &. So, you can do something like:

    Dial(technology/100&technology/101&technology/102) to simultaneously ring phones 100, 101 and 102. They don't even have to be on the same tech stack. Sweet!

    So, in this case you can do something like:

    Dial(PJSIP/100@192.168.1.14&PJSIP/100@10.0.0.43) and there you go! It's usually a bit more complicated than that since every contact tends to have a unique tag at the end so it can be identified as it changes IP addresses etc. But there's a handy function called PJSIP_DIAL_CONTACTS that will generate this mess for you, so all you do is:

    Dial(${PJSIP_DIAL_CONTACTS(100)}) and bam! It works! Yay!

    Ok, so, why this huge wall'o'text? Because now you can finally understand why the following hackery is needed. It's queues. It's always fucking queues. See, the Queue application internally just calls an equivalent of Dial. So, if you add PJSIP/100 to a queue, it will just do a Dial(PJSIP/100) internally, meaning only one of the phones will ring. Which is no good. You can't tell it what to run instead, so, you're boned.

    0_1530205768305_fe87c6e3-15f1-427e-a097-93d8ba93c15c-image.png

    Time for hackery! See, Asterisk has one more type of channel, one that doesn't rely on any other technology. A Local channel. It's something Asterisk can manipulate internally and can come in handy at times. So, here's the plan. First, we create a context for our agents in queue:

    context queue-agents {
        _X. => {
            Dial(${PJSIP_DIAL_CONTACTS(${EXTEN})});
        }
    }
    

    _X. means this will execute as long as you call anything starting with a digit in that context. The EXTEN variable is usually whatever the user dialed, but in this particular case it will be the Queue application dialing it. The principle is the same. So, we add this to our queue instead:

    queue add member Local/100@queue-agents/n to MyQueue - where /n part means "automatically answer all requests". Because of course that's what /n means, what are you, stupid?

    But, damn it, now it's always available. Because Local channels always are. Ok, ok, we can do something about that, we'll force it to use the state of the endpoint instead:

    queue add member Local/100@queue-agents/n to MyQueue as 100 penalty 0 state_interface hint:PJSIP/100

    Now, that's not confusing, right? Oh, yeah, you have to give it an alias and a penalty even if you don't care, because the parser is an idiot and requires all the arguments in that order, and you can't skip any, EVEN THOUGH THEY ARE NAMED. And hint:PJSIP/100 means "report the state of PJSIP/100 instead of the local channel". And this is, BTW, documented exactly nowhere, I lucked out on finding the example of this in some unrelated piece of code somewhere online. I have no idea how that poor soul figured that out, but I salute them as I drink myself into a coma. And for both of you smartasses who are about to say I should just use AddQueueMember application from the dialplan and just pass empty parameters for those I don't care about, shush, I'm ranting!

    Now you're like, Onyx, you fuck, I read this wall of text and the amount of :wtf:ery wasn't worth it! I want my internetpointzzz back! Hold your disco horses, now we're going into what this shit does internally.

    So, say someone calls in to your queue. Your phone rings and you answer. What happens internally?

    1. A channel for the caller is created, called something like PJSIP/providername-randomgibberish
    2. A local channel is created, called something like Local/100@queue-agents-otherrandomgibberish;1
    3. Another local channel is created, called something like Local/100@queue-agents-otherrandomgibberish;2
    4. A channel for the callee is created, called something like PJSIP/100-differentothergibberish
    5. Caller is bridged to one of the local channels, let's say Local/100@queue-agents-otherrandomgibberish;1
    6. Caller is bridged to one of the local channels, let's say Local/100@queue-agents-otherrandomgibberish;2
    7. A local bridge is created between Local/100@queue-agents-otherrandomgibberish;1 and Local/100@queue-agents-otherrandomgibberish;2
    8. You are now sorry you answered the call because the caller is a prick

    So yeah, two extra channels are made, just so they can be bridged together. Fun. I mean, it works, it's not the worst thing in the world. But then you're trying to figure out who the fuck is talking to whom in realtime, just sniffing Asterisk's asstelnet output and you scratch your fucking head. "Regular" bridges have BridgeCreate, BridgeEnter and BridgeLeave events. And every bridge has a unique ID, so you can easily identify it and track it. Local bridges just pop out of nowhere. They have no unique ID, because fuck you. But you still need to track them and hop over them just to figure out who's on the other side of the call.

    And I had to reverse engineer and figure out most of this shit on my own. Maybe that's why I'm angry and this is not so bad? Dunno. You tell me.


  • :belt_onion:

    @onyx I can tell you I got about halfway through that before saying "Fuck this." (Yes, I did continue through to the end.)

    Is the reason people use this that it's free/OSS? Is that really it? Or is there no viable competitor in the field? Because fuck that.


  • BINNED

    @heterodox said in Asterisk PBX:

    Is the reason people use this that it's free/OSS? Is that really it?

    Pretty much. Also, most commercial offerings are locked down and have shitty APIs, so if their GUI doesn't let you change stuff, tough shit (note: I'm mostly buttuming here because I'm unaware of many people doing complex integrations, and most of them won't let you even see the API docs without buying a licence so I can't really even read it to get a feel for it).

    @heterodox said in Asterisk PBX:

    Or is there no viable competitor in the field?

    I'm hearing good things about FreeSwitch. Sadly, we have no time to spare on researching if we should switch stacks, hopefully we will manage to allocate some resources to that come Autumn.

    @heterodox said in Asterisk PBX:

    Because fuck that.

    To be fair, some of the shit is just inherent to the whole telephony thing in general. But they also fucked up some implementation details to be sure.


  • Java Dev

    @onyx Thanks for convincing me to stay doing sane things. Like fixing bugs in implementations of badly-written TLS RFCs.


Log in to reply