package org.freertr.clnt; import java.util.ArrayList; import java.util.List; import org.freertr.addr.addrIP; import org.freertr.cfg.cfgDial; import org.freertr.cfg.cfgIfc; import org.freertr.cfg.cfgVrf; import org.freertr.enc.encCallConn; import org.freertr.enc.encCallHnd; import org.freertr.enc.encCallOne; import org.freertr.enc.encCodec; import org.freertr.enc.encCodecG711aLaw; import org.freertr.enc.encCodecG711uLaw; import org.freertr.ip.ipFwd; import org.freertr.ip.ipFwdIface; import org.freertr.ip.ipFwdTab; import org.freertr.pack.packHolder; import org.freertr.pack.packIax; import org.freertr.pipe.pipeLine; import org.freertr.pipe.pipeSide; import org.freertr.prt.prtGen; import org.freertr.tab.tabGen; import org.freertr.util.bits; import org.freertr.util.debugger; import org.freertr.util.logger; import org.freertr.util.notifier; import org.freertr.util.syncInt; /** * inter asterisk exchange (rfc5456) client * * @author matecsaba */ public class clntIax implements Runnable, encCallHnd { /** * create instance */ public clntIax() { } /** * upper */ public cfgDial upper; /** * client or server */ public boolean client; /** * local port */ public int portLoc = 0; /** * remote port */ public int portRem = packIax.port; /** * codec, true=alaw, false=ulaw */ public boolean aLaw = true; /** * keepalive interval */ public int keepalive = 0; /** * register interval */ public int register = 0; /** * number of tries */ public int retry = 8; /** * timeout in seconds */ public int timeout = 5; /** * vrf of target */ public cfgVrf vrf = null; /** * source interface */ public cfgIfc srcIfc = null; /** * target */ public String trgDom; /** * username */ public String usr; /** * udp handler */ protected prtGen udp; /** * forwarder interface */ protected ipFwdIface srcFwd; /** * connection to peer */ protected pipeSide conn; private final tabGen cls = new tabGen(); private ipFwd fwd; private addrIP trgAdr; /** * keepalive */ protected clntIaxKeep timKeep; /** * need to run */ protected boolean need2run; private int seqRx; private int seqTx; /** * start work */ public void startWork() { need2run = true; seqRx = 0; seqTx = 0; logger.startThread(this); if ((keepalive + register) < 1) { return; } timKeep = new clntIaxKeep(this); timKeep.start(); } /** * stop work */ public void stopWork() { need2run = false; timKeep = null; if (conn != null) { conn.setClose(); } conn = null; for (int i = 0; i < cls.size(); i++) { try { cls.get(i).setClose(); } catch (Exception e) { } } } /** * get codec * * @return codec */ protected encCodec getCodec() { if (aLaw) { return new encCodecG711aLaw(); } else { return new encCodecG711uLaw(); } } private clntIaxCall findLocCid(int o) { for (int i = 0; i < cls.size(); i++) { clntIaxCall ntry = cls.get(i); if (ntry.lid == o) { return ntry; } } return null; } private int getLocCid() { for (;;) { int o = bits.random(0x10, 0x7ff0); if (findLocCid(o) == null) { return o; } } } public void run() { if (debugger.clntIaxTraf) { logger.debug("started"); } for (;;) { bits.sleep(1000); if (!need2run) { break; } try { doWork(); } catch (Exception e) { logger.traceback(e); } } if (debugger.clntIaxTraf) { logger.debug("stopped"); } } /** * delete the call * * @param leg call id */ protected void delCall(clntIaxCall leg) { cls.del(leg); } /** * send register * * @param wau authentication request * @param pau authentication request */ protected void sendReg(String wau, String pau) { if (!client) { return; } if (conn == null) { return; } packHolder pck = new packHolder(true, true); packIax rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_rrq; rep.sid = 0; rep.tid = 0; rep.frsh = register / 1000; rep.user = "" + usr; if (debugger.clntIaxTraf) { rep.dump("tx"); } pck.clear(); rep.placeTlvs(pck); rep.sendPack(pck); } /** * send keepalive */ protected void sendKeep() { if (conn == null) { return; } packHolder pck = new packHolder(true, true); packIax rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_pok; rep.sid = 0; rep.tid = 0; rep.frsh = register / 1000; rep.user = "" + usr; if (debugger.clntIaxTraf) { rep.dump("tx"); } pck.clear(); rep.placeTlvs(pck); rep.sendPack(pck); } /** * get number of in calls * * @param dir direction, true=in, false=out * @return number of calls */ public int numCalls(boolean dir) { int o = 0; for (int i = 0; i < cls.size(); i++) { if (cls.get(i).dir == dir) { o++; } } return o; } /** * get number of out messages * * @param dir direction, true=in, false=out * @return number of messages */ public int numMsgs(boolean dir) { return 0; } /** * check if ready * * @return true if yes, false if no */ public boolean isReady() { if (conn == null) { return false; } if (conn.isClosed() != 0) { return false; } return conn.isReady() == 3; } /** * get call list * * @param dir direction, true=in, false=out * @return list */ public List listCalls(boolean dir) { List res = new ArrayList(); for (int i = 0; i < cls.size(); i++) { clntIaxCall ntry = cls.get(i); if (ntry == null) { continue; } if (ntry.dir != dir) { continue; } res.add(ntry.getCid() + "|" + ntry.src + "|" + ntry.trg + "|" + bits.timePast(ntry.started)); } return res; } /** * send message * * @param calling calling number * @param called called number * @param msg message * @return false on success, true on error */ public boolean sendMsg(String calling, String called, List msg) { return true; } /** * make the call * * @param calling calling number * @param called called number * @return call id, null if error */ public String makeCall(String calling, String called) { clntIaxCall ntry = new clntIaxCall(this, 0); ntry.lid = getLocCid(); ntry.rid = -ntry.lid; ntry.dir = false; ntry.src = calling; ntry.trg = called; ntry.alloc(); cls.put(ntry); if (ntry.doOut()) { if (upper != null) { upper.stoppedCall(true, ntry.src, ntry.trg, ntry.started); } delCall(ntry); return null; } return ntry.getCid(); } /** * stop the call * * @param id call id */ public void stopCall(String id) { clntIaxCall ntry = findFullCid(id); if (ntry == null) { return; } ntry.doHup(); ntry.setClose(); if (ntry.dir) { return; } if (upper != null) { upper.stoppedCall(true, ntry.src, ntry.trg, ntry.started); } delCall(ntry); } private clntIaxCall findFullCid(String cid) { if (cid == null) { return null; } int i = cid.indexOf("-"); if (i < 0) { return null; } clntIaxCall ntry = new clntIaxCall(this, bits.str2num(cid.substring(0, i))); ntry = cls.find(ntry); if (ntry == null) { return null; } if (bits.str2num(cid.substring(i + 1, cid.length())) != ntry.lid) { return null; } return ntry; } /** * get call * * @param cid call id * @return rtp */ public encCallOne getCall(String cid) { return findFullCid(cid); } /** * do one round */ public void doWork() { if (conn != null) { conn.setClose(); } conn = null; trgAdr = clntDns.justResolv(trgDom, 0); if (trgAdr == null) { return; } fwd = vrf.getFwd(trgAdr); udp = vrf.getUdp(trgAdr); if (srcIfc != null) { srcFwd = srcIfc.getFwdIfc(trgAdr); } else { srcFwd = ipFwdTab.findSendingIface(fwd, trgAdr); } if (srcFwd == null) { return; } conn = udp.streamConnect(new pipeLine(65536, true), srcFwd, portLoc, trgAdr, portRem, "iax", -1, null, -1, -1); if (conn == null) { return; } conn.setTime(180000); conn.lineRx = pipeSide.modTyp.modeCRtryLF; conn.lineTx = pipeSide.modTyp.modeCRLF; conn.setReady(); conn.wait4ready(30000); if (conn.isReady() != 3) { conn.setClose(); conn = null; return; } packIax iax = new packIax(conn); packHolder pck = new packHolder(true, true); for (;;) { if (!need2run) { break; } if (conn == null) { break; } if (iax.isClosed() != 0) { break; } if (iax.recvPack(pck) < 0) { continue; } clntIaxCall ntry = new clntIaxCall(this, iax.sid); clntIaxCall old = cls.find(ntry); if ((iax.typ == packIax.typ_voc) && (old != null)) { if (iax.tid == -1) { byte[] buf = pck.getCopy(); old.bufT.nonBlockPut(buf, 0, buf.length); continue; } old.seqI = iax.seqO; old.seqO = iax.seqI; if (iax.tid == old.lid) { if (debugger.clntIaxTraf) { iax.dump("rx"); } byte[] buf = pck.getCopy(); old.bufT.nonBlockPut(buf, 0, buf.length); iax.typ = packIax.typ_iax; iax.sub = packIax.iam_ack; //iax.times = iax.times; iax.sid = old.lid; iax.tid = old.rid; iax.seqI = old.seqI + 1; iax.seqO = old.seqO; if (debugger.clntIaxTraf) { iax.dump("tx"); } pck.clear(); iax.sendPack(pck); continue; } } if (iax.tid == -1) { continue; } if (debugger.clntIaxTraf) { iax.dump("rx"); } iax.parseTlvs(pck); if (old != null) { old.seqI = iax.seqO; old.seqO = iax.seqI; } packIax rep = null; switch ((iax.typ << 8) | iax.sub) { case (packIax.typ_iax << 8) | packIax.iam_rrq: // register req if (client) { break; } rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_rak; rep.sid = 0; rep.tid = iax.sid; if (register > 0) { rep.frsh = register / 1000; } else { rep.frsh = iax.frsh; } rep.user = iax.user; break; case (packIax.typ_iax << 8) | packIax.iam_rrl: // register rel rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_rak; rep.sid = 0; rep.tid = iax.sid; break; case (packIax.typ_iax << 8) | packIax.iam_rak: // register ack rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = iax.tid; rep.tid = iax.sid; break; case (packIax.typ_iax << 8) | packIax.iam_ack: // acknowledge if (old == null) { continue; } if (old.que == iax.seqI) { old.que = -1; old.notif.wakeup(); } continue; case (packIax.typ_iax << 8) | packIax.iam_new: // new call if (old == null) { old = ntry; if (upper == null) { rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_rej; rep.sid = 0; rep.tid = iax.sid; break; } old.dir = true; old.src = iax.calling; old.trg = iax.called; old.lid = getLocCid(); old.alloc(); old.start(); cls.put(old); } rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_acc; rep.times = (int) (bits.getTime() - old.started); rep.codecD = upper.getCodec().getIAXtype(); rep.sid = old.lid; rep.tid = old.rid; break; case (packIax.typ_iax << 8) | packIax.iam_acc: // accept call if (old == null) { old = findLocCid(iax.tid); } if (old == null) { break; } if (old.dir) { break; } delCall(old); old.rid = iax.sid; cls.put(old); old.notif.wakeup(); rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = old.lid; rep.tid = old.rid; break; case (packIax.typ_iax << 8) | packIax.iam_rej: // reject call if (old == null) { old = findLocCid(iax.tid); } if (old == null) { break; } if (old.dir) { break; } delCall(old); old.setClose(); old.notif.wakeup(); rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = old.lid; rep.tid = old.rid; if (upper != null) { upper.stoppedCall(true, old.src, old.trg, old.started); } delCall(old); break; case (packIax.typ_iax << 8) | packIax.iam_hup: // hangup call case (packIax.typ_ctr << 8) | packIax.ctr_hng: // soft hangup if (old == null) { break; } old.setClose(); old.stopping = true; old.notif.wakeup(); rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = old.lid; rep.tid = old.rid; if (old.dir) { break; } if (upper != null) { upper.stoppedCall(true, old.src, old.trg, old.started); } delCall(old); break; case (packIax.typ_ctr << 8) | packIax.ctr_ans: // answer call if (old == null) { break; } if (old.dir) { break; } old.que = -2; old.notif.wakeup(); rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = old.lid; rep.tid = old.rid; break; case (packIax.typ_ctr << 8) | packIax.ctr_rig: // remote ring case (packIax.typ_ctr << 8) | packIax.ctr_rng: // local ring if (old == null) { break; } rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = old.lid; rep.tid = old.rid; break; case (packIax.typ_iax << 8) | packIax.iam_vnk: // vnak if (old == null) { break; } old.full = false; rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_pon; rep.times = (int) (bits.getTime() - old.started); rep.sid = old.lid; rep.tid = old.rid; break; case (packIax.typ_iax << 8) | packIax.iam_pin: // ping call if (old == null) { break; } rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_pon; rep.times = (int) (bits.getTime() - old.started); rep.sid = old.lid; rep.tid = old.rid; break; case (packIax.typ_iax << 8) | packIax.iam_pok: // poke rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_pon; rep.times = iax.times; rep.sid = iax.tid; rep.tid = iax.sid; break; case (packIax.typ_iax << 8) | packIax.iam_pon: // pong call if (old != null) { rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = old.lid; rep.tid = old.rid; break; } if (iax.tid != 0) { break; } // pong session rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = iax.tid; rep.tid = iax.sid; break; case (packIax.typ_iax << 8) | packIax.iam_inv: // invalid message case (packIax.typ_iax << 8) | packIax.iam_uns: // unsupported message rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_ack; rep.times = iax.times; rep.sid = iax.tid; rep.tid = iax.sid; break; } if (rep == null) { rep = new packIax(conn); rep.typ = packIax.typ_iax; rep.sub = packIax.iam_inv; rep.sid = 0; rep.tid = iax.sid; } rep.seqI = iax.seqO + 1; rep.seqO = iax.seqI; if (debugger.clntIaxTraf) { rep.dump("tx"); } pck.clear(); rep.placeTlvs(pck); rep.sendPack(pck); } if (conn != null) { conn.setClose(); } conn = null; if (debugger.clntIaxTraf) { logger.debug("restarting"); } } } class clntIaxKeep implements Runnable { private final clntIax lower; public clntIaxKeep(clntIax parent) { lower = parent; } public void start() { logger.startThread(this); } public void run() { try { long lastKep = 0; long lastReg = 0; for (;;) { bits.sleep(1000); if (lower.timKeep != this) { break; } if (!lower.need2run) { break; } long tim = bits.getTime(); if ((lower.keepalive > 0) && ((tim - lastKep) > lower.keepalive)) { lower.sendKeep(); lastKep = tim; } if ((lower.register > 0) && ((tim - lastReg) > lower.register)) { lower.sendReg(null, null); lastReg = tim; } } } catch (Exception e) { logger.traceback(e); } } } class clntIaxCall implements Runnable, Comparable, encCallOne { public final clntIax lower; public final long started; public notifier notif; public boolean need2run; public boolean dir; public int seqO; public int seqI; public int que; public int rid; public int lid; public String src; public String trg; public String newSrc; public String newTrg; public cfgDial peer; public String rcd; public packIax snd; public encCallOne call; public encCallConn conner; public encCodec codec; public pipeLine bufP; public pipeSide bufR; public pipeSide bufT; public boolean full; public boolean conned; public boolean stopping; public clntIaxCall(clntIax prnt, int rem) { lower = prnt; rid = rem; started = bits.getTime(); } public int compareTo(clntIaxCall o) { if (rid < o.rid) { return -1; } if (rid > o.rid) { return +1; } return 0; } public void alloc() { need2run = true; que = -1; notif = new notifier(); snd = new packIax(lower.conn); bufP = new pipeLine(32768, true); bufR = bufP.getSide(); bufT = bufP.getSide(); full = false; } public void start() { logger.startThread(this); } public void run() { try { doIn(); } catch (Exception e) { logger.traceback(e); } } public String getCid() { return rid + "-" + lid; } public boolean doCtr(int typ, int sub) { packIax iax = new packIax(lower.conn); packHolder pck = new packHolder(true, true); for (int i = 0; i < lower.retry; i++) { iax.clear(); pck.clear(); iax.typ = typ; iax.sub = sub; iax.seqI = seqI; iax.seqO = seqO; iax.sid = lid; iax.tid = rid; iax.times = (int) (bits.getTime() - started); que = (seqO + 1) & 0xff; if (debugger.clntIaxTraf) { iax.dump("tx"); } iax.sendPack(pck); notif.sleep(lower.timeout * 1000); if (que < 0) { return false; } } return true; } public void doHup() { if (stopping) { return; } stopping = true; need2run = false; doCtr(packIax.typ_iax, packIax.iam_hup); } public boolean doOut() { codec = lower.getCodec(); packIax iax = new packIax(lower.conn); packHolder pck = new packHolder(true, true); for (int i = 0; i < lower.retry; i++) { if (!need2run) { break; } iax.clear(); pck.clear(); iax.typ = packIax.typ_iax; iax.sub = packIax.iam_new; iax.seqI = 0; iax.seqO = 0; iax.sid = lid; iax.tid = 0; iax.times = (int) (bits.getTime() - started); iax.proto = 2; iax.user = "" + lower.usr; iax.calling = "" + src; iax.called = "" + trg; iax.codecC = codec.getIAXtype(); iax.codecD = iax.codecC; if (debugger.clntIaxTraf) { iax.dump("tx"); } iax.placeTlvs(pck); iax.sendPack(pck); notif.sleep(lower.timeout * 1000); if (rid > 0) { break; } } if (rid < 0) { return true; } for (int i = 0; i < lower.retry; i++) { if (!need2run) { break; } notif.sleep(lower.timeout * 1000); if (que == -2) { conned = true; return false; } } doHup(); return true; } public void doIn() { newSrc = lower.upper.incomeSrc(src); newTrg = lower.upper.incomeTrg(trg); peer = lower.upper.incomeCall(newSrc, newTrg); if (peer == null) { doHup(); lower.upper.stoppedCall(false, newSrc, newTrg, started); lower.delCall(this); return; } rcd = peer.makeCall(newSrc, newTrg); if (rcd == null) { doHup(); lower.upper.stoppedCall(false, newSrc, newTrg, started); lower.delCall(this); return; } doCtr(packIax.typ_ctr, packIax.ctr_ans); codec = lower.getCodec(); call = peer.getCall(rcd); conner = new encCallConn(this, call, codec, peer.getCodec()); conned = true; for (;;) { if (conner.isClosed() != 0) { break; } if (lower.conn == null) { break; } if (!need2run) { break; } bits.sleep(1000); } conner.setClose(); peer.stopCall(rcd); doHup(); lower.upper.stoppedCall(false, newSrc, newTrg, started); lower.delCall(this); } public void setClose() { need2run = false; if (bufP != null) { bufP.setClose(); } } public int isClosed() { if (!need2run) { return 3; } return bufR.isClosed(); } public void sendPack(packHolder pck) { if (!need2run) { return; } if (!conned) { return; } snd.clear(); pck.merge2beg(); snd.times = (int) (bits.getTime() - started); snd.sid = lid; if (full) { snd.tid = -1; snd.sendPack(pck); return; } snd.tid = rid; snd.typ = packIax.typ_voc; snd.sub = codec.getIAXtype(); snd.seqI = seqI; snd.seqO = seqO; full = true; seqO++; if (debugger.clntIaxTraf) { snd.dump("tx"); } snd.sendPack(pck); } public int recvPack(packHolder pck, boolean blocking, boolean enforce) { int i; if (blocking) { i = 143; } else { i = 142; } return pck.pipeRecv(bufR, 0, -1, i); } }