0

I'm using Jain Sip and my use-case is this: One registrar, one caller and one callee. Caller and Callee register themselves with the registrar. The caller then initiates a call. The Invite reaches my callee but when I'm trying to send a "RINGING" response, it doesn't reach my caller.

Registrar: 172.18.18.109:5060

Caller: 172.18.18.52:5066

Callee: 172.18.18.52:5067

enter image description here

Here's my Code:

Caller:

import mypackage.voip.model.ClientStatus;
import gov.nist.javax.sip.address.AddressFactoryImpl;
import gov.nist.javax.sip.header.HeaderFactoryImpl;
import gov.nist.javax.sip.message.MessageFactoryImpl;
import javax.sdp.*;
import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.*;

import static mypackage.voip.model.ClientStatus.*;

public class Caller implements SipListener {
    private String myIpAddress = "172.18.18.52";
    private String registrarIp = "172.18.18.109";
    private int myPort = 5066;
    private String myTransportProtocol = "UDP";
    private String myUserName = "caller";
    private String viaHostName = registrarIp;
    private int viaHostPort = 5060;
    private AddressFactoryImpl addressFactory;
    private SipStack sipStack;
    private SipProvider sipProvider = null;
    private MessageFactoryImpl messageFactory;
    private HeaderFactoryImpl headerFactory;
    private ClientStatus clientStatus = DISCONNECTED;
    private String myDomainName = "172.18.18.52";
    private String viaHostBranch = "abcdefghi";

    public Caller(String name, String ipAddress, int port, String protocol) {
        this.myUserName = name;
        this.myTransportProtocol = protocol;
        this.myIpAddress = ipAddress;
        this.myPort = port;
        changeClientStatusTo(DISCONNECTED);
    }

    public void init() throws PeerUnavailableException, TooManyListenersException {
        SipFactory sipFactory = SipFactory.getInstance();
        Properties properties = new Properties();
        properties.setProperty("javax.sip.STACK_NAME", myIpAddress + ":" + myPort);
        sipStack = sipFactory.createSipStack(properties);
        addressFactory = new AddressFactoryImpl();
        messageFactory = new MessageFactoryImpl();
        headerFactory = new HeaderFactoryImpl();
        sipProvider = getSipProvider();

    }

    public void doRegistration(String registrarIp) throws ParseException, InvalidArgumentException {
        if (startRegistration(registrarIp)) {
            changeClientStatusTo(REGISTERING);
        }
    }
    private SipProvider getSipProvider() {
        try {
            ListeningPoint listeningPoint = sipStack.createListeningPoint(myIpAddress, myPort, myTransportProtocol);

            if (sipProvider != null)
                return sipProvider;
            sipProvider = sipStack.createSipProvider(listeningPoint);
            sipProvider.addSipListener(this);
            return sipProvider;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private void changeClientStatusTo(ClientStatus newClientStatus) {
        this.clientStatus = newClientStatus;
    }
    public Address getMyContactAddress() {
        return createAddress(myUserName, myIpAddress, myPort);
    }
    private Address createAddress(String username, String hostname, int port) {
        try {
            return addressFactory.createAddress(String.format("%s <sip:%s@%s:%s;transport=%s>",
                    username,
                    username,
                    hostname,
                    port,
                    myTransportProtocol));
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
    private boolean startRegistration(String registrarIpAddress) throws ParseException, InvalidArgumentException {
        SipURI requestURI = addressFactory.createSipURI("sip:" + registrarIpAddress + ":5060");

        CallIdHeader callIdHeader = headerFactory.createCallIdHeader("callID");
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(2L, Request.REGISTER);

        ContactHeader contactHeader = headerFactory.createContactHeader(getMyContactAddress());
        ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(217);

        Address fromAddress = getMyContactAddress();
        Address toAddress = fromAddress;

        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, "tag");
        ToHeader toHeader = headerFactory.createToHeader(toAddress, "tag");

        List viaHeaders = Arrays.asList();
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        Request register = messageFactory.createRequest(requestURI,
                Request.REGISTER,
                callIdHeader,
                cSeqHeader,
                fromHeader,
                toHeader,
                viaHeaders,
                maxForwards);
        register.addHeader(contactHeader);
        register.addHeader(expiresHeader);
        return sendRequest(register);
    }

    private boolean sendRequest(Request request) {
        sipProvider = getSipProvider();
        try {
            ClientTransaction clientTransaction = sipProvider.getNewClientTransaction(request);
            clientTransaction.sendRequest();
            return true;
        } catch (SipException e) {
            e.printStackTrace();
        }
        return false;
    }



    private boolean sendInvite(Address addressToInvite) throws SdpException {
        try {
            CallIdHeader callIdHeader = headerFactory.createCallIdHeader("callID");
            CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(System.currentTimeMillis() / 1000L, Request.INVITE);

            Address fromAddress = getMySipAddress();
            FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, "meinTag");
            ToHeader toHeader = headerFactory.createToHeader(addressToInvite, null);

            ViaHeader viaHeader = headerFactory.createViaHeader(viaHostName,
                    viaHostPort,
                    myTransportProtocol,
                    viaHostBranch);
            List viaHeaders = Arrays.asList(viaHeader);

            Address sipAd = addressFactory.createAddress("sip:" + toHeader.getAddress().getDisplayName() + "@" + registrarIp);
            MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
            Request invite = messageFactory.createRequest(sipAd.getURI(),
                    Request.INVITE,
                    callIdHeader,
                    cSeqHeader,
                    fromHeader,
                    toHeader,
                    viaHeaders,
                    maxForwards);

            AllowHeader allowHeader = headerFactory.createAllowHeader("PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS");
            invite.addHeader(allowHeader);

            ContactHeader contactHeader = headerFactory.createContactHeader(toHeader.getAddress());
            invite.addHeader(contactHeader);

            SessionDescription sessionDescription = SdpFactory.getInstance().createSessionDescription();
            String sdpData =
                    "v=0\n" +
                            "o=Lautsprecher 13760799956958020 13760799956958020 IN IP4 172.18.18.52\n" +
                            "s=-\n" +
                            "c=IN IP4 172.18.18.52\n" +
                            "m=audio 49172 RTP/AVP 11\n" +
                            "a=rtpmap:11 L16/44100";
            byte[] contents = sdpData.getBytes();

            ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp");
            invite.setContent(contents, contentTypeHeader);

            return sendRequest(invite);
        } catch (ParseException | InvalidArgumentException e) {
            return false;
        }
    }
    public Address getMySipAddress() {
        return createSipUserAddress(myUserName, myDomainName);
    }

    private Address createSipUserAddress(String name, String domain) {
        try {
            return addressFactory.createAddress(String.format("%s <sip:%s@%s;transport=%s>",
                    name, name, domain, myTransportProtocol));
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
    @Override
    public void processRequest(RequestEvent requestEvent) {

    }

    @Override
    public void processResponse(ResponseEvent responseEvent) {
        Response response = responseEvent.getResponse();
    String method = responseEvent.getClientTransaction().getRequest().getMethod();
    int statusCode = responseEvent.getResponse().getStatusCode();

        if (statusCode == 200 && method.equalsIgnoreCase("REGISTER")) {
        changeClientStatusTo(REGISTERED);
    }

        if (statusCode == 100 && method.equalsIgnoreCase("INVITE")) {
        changeClientStatusTo(TRYING);
    }

        if (statusCode == 180 && method.equalsIgnoreCase("INVITE")) {
        changeClientStatusTo(RINGING);
    }

        if (statusCode == 200 && method.equalsIgnoreCase("INVITE")) {
        // DO SOMETHING
    }
}

    @Override
    public void processTimeout(TimeoutEvent timeoutEvent) { }

    @Override
    public void processIOException(IOExceptionEvent exceptionEvent) { }

    @Override
    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { }

    @Override
    public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { }
}

Callee:

import mypackage.voip.model.ClientStatus;
import gov.nist.javax.sip.address.AddressFactoryImpl;
import gov.nist.javax.sip.header.HeaderFactoryImpl;
import gov.nist.javax.sip.header.SIPHeaderNames;
import gov.nist.javax.sip.message.MessageFactoryImpl;

import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.TooManyListenersException;

import static mypackage.voip.model.ClientStatus.DISCONNECTED;
import static mypackage.voip.model.ClientStatus.INVITED;
import static mypackage.voip.model.ClientStatus.REGISTERING;
import static javax.sip.message.Response.OK;
import static javax.sip.message.Response.RINGING;

public class Callee implements SipListener {
    private int myPort;
    private String myIpAddress;
    private String myUserName;
    private ClientStatus clientStatus = DISCONNECTED;
    private SipStack sipStack;
    private SipProvider sipProvider = null;
    private MessageFactoryImpl messageFactory;
    private HeaderFactoryImpl headerFactory;
    private AddressFactoryImpl addressFactory;
    private String myTransportProtocol = "UDP";

    public Callee(String name, String ip, int port, String protocol) {
        this.myUserName = name;
        this.myTransportProtocol = protocol;
        this.myIpAddress = ip;
        this.myPort = port;
        changeClientStatusTo(DISCONNECTED);
    }

    void init() throws PeerUnavailableException, TooManyListenersException {
        SipFactory sipFactory = SipFactory.getInstance();
        Properties properties = new Properties();
        properties.setProperty("javax.sip.STACK_NAME", myIpAddress + ":" + myPort);
        addressFactory = new AddressFactoryImpl();
        messageFactory = new MessageFactoryImpl();
        headerFactory = new HeaderFactoryImpl();
        sipProvider = getSipProvider();
    }

    @Override
    public void processRequest(RequestEvent requestEvent) {
        Request request = requestEvent.getRequest();
        Header from = request.getHeader(SIPHeaderNames.FROM);
        Header to = request.getHeader(SIPHeaderNames.TO);
        Header contact = request.getHeader(SIPHeaderNames.CONTACT);
        Header via = request.getHeader(SIPHeaderNames.VIA);

        ServerTransaction serverTransactionId = requestEvent.getServerTransaction();
        if (request.getMethod().equals(Request.INVITE)) {
            processInvite(requestEvent);
        } else if (request.getMethod().equals(Request.ACK)) {
            // DO SOMETHING
        } else if (request.getMethod().equals(Request.CANCEL)) {
            // DO SOMETHING
        } else if (request.getMethod().equals(Request.BYE)) {
            // DO SOMETHING
        }
        else{
                // DO SOMETHING
            }
    }

    @Override
    public void processResponse(ResponseEvent responseEvent) {

    }

    public void doRegistration(String registrarIp) throws ParseException, InvalidArgumentException {
        if (startRegistration(registrarIp)) {
            changeClientStatusTo(REGISTERING);
        }
    }

    private void changeClientStatusTo(ClientStatus newClientStatus) {
        this.clientStatus = newClientStatus;
    }

    private boolean startRegistration(String registrarIpAddress) throws ParseException, InvalidArgumentException {

        CallIdHeader callIdHeader = headerFactory.createCallIdHeader("callID");
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(System.currentTimeMillis() / 1000L, Request.REGISTER);

        ContactHeader contactHeader = headerFactory.createContactHeader(getMyContactAddress());
        ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(217);

        Address fromAddress = getMyContactAddress();
        Address toAddress = fromAddress;

        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, "tag");
        ToHeader toHeader = headerFactory.createToHeader(toAddress, "tag");

        List viaHeaders = Arrays.asList();

        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);


        SipURI requestURI = addressFactory.createSipURI("sip:" + registrarIpAddress + ":5060");

        Request register = messageFactory.createRequest(requestURI,
                Request.REGISTER,
                callIdHeader,
                cSeqHeader,
                fromHeader,
                toHeader,
                viaHeaders,
                maxForwards);
        register.addHeader(contactHeader);
        register.addHeader(expiresHeader);
        return sendRequest(register);
    }

    private Address getMyContactAddress() {
        return createAddress(myUserName, myIpAddress, myPort);
    }

    private boolean sendRequest(Request request) {
        sipProvider = getSipProvider();
        try {
            ClientTransaction clientTransaction = sipProvider.getNewClientTransaction(request);
            clientTransaction.sendRequest();
            return true;
        } catch (SipException e) {
        e.printStackTrace();
        }
        return false;
    }

    private Address createAddress(String username, String hostname, int port) {
        try {
            return addressFactory.createAddress(String.format("%s <sip:%s@%s:%s;transport=%s>",
                    username,
                    username,
                    hostname,
                    port,
                    myTransportProtocol));
        } catch (ParseException e) {
            return null;
        }
    }

    private void processInvite(RequestEvent requestEvent) {
        try {
            changeClientStatusTo(INVITED);
            SipProvider sipProvider = (SipProvider) requestEvent.getSource();
            Request request = requestEvent.getRequest();

            ServerTransaction st = requestEvent.getServerTransaction();
            if (st == null) {
                st = sipProvider.getNewServerTransaction(request);
            }

            Response ringingResponse = messageFactory.createResponse(RINGING, request);
            ToHeader toHeader = (ToHeader) ringingResponse.getHeader(ToHeader.NAME);
            toHeader.setTag("foobar");
            ringingResponse.removeHeader(ToHeader.NAME);
            ringingResponse.addHeader(toHeader);
            st.sendResponse(ringingResponse);

        } catch (ParseException | InvalidArgumentException | SipException e) {
            e.printStackTrace();
        }
    }

    public ClientStatus getClientStatus() {
        return clientStatus;
    }

    private SipProvider getSipProvider() {
        try {
            ListeningPoint listeningPoint = sipStack.createListeningPoint(myIpAddress, myPort, myTransportProtocol);

            if (sipProvider != null)
                return sipProvider;

            sipProvider = sipStack.createSipProvider(listeningPoint);
            sipProvider.addSipListener(this);
            return sipProvider;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    @Override
    public void processTimeout(TimeoutEvent timeoutEvent) {
    }

    @Override
    public void processIOException(IOExceptionEvent exceptionEvent) {
    }

    @Override
    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
    }

    @Override
    public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
    }
}

EDIT:

I wrote a little Test for my scenario:

import org.junit.Before;
import org.junit.Test;

import javax.sdp.SdpException;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.TooManyListenersException;

import static mypackage.voip.model.ClientStatus.*;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.CoreMatchers.is;

public class CallerCalleeTest {

    private String myIpAddress;
    private String registrarIpAddress = "172.18.18.109";
    private TestRegistrar testRegistrar;
    @Test
    public void testTwoClients() throws PeerUnavailableException, UnknownHostException, TooManyListenersException,
            SdpException, ParseException, InvalidArgumentException {
        Caller caller = createAndInitializeCaller("caller", myIpAddress, 5066);
        caller.doRegistration(registrarIpAddress);
        await().until(() -> caller.getClientStatus(), is(REGISTERED));

        Callee callee = createAndInitializeCallee("callee", myIpAddress, 5067);
        callee.doRegistration(registrarIpAddress);
        await().until(() -> callee.getClientStatus(), is(REGISTERING));

        // Start the call
        caller.call("callee", myIpAddress + ":" + 5067);
        await().until(() -> caller.getClientStatus(), is(WAIT_AFTER_INVITE));
        await().until(() -> callee.getClientStatus(), is(INVITED));
    }

    @Before
    public void setup() throws UnknownHostException, PeerUnavailableException, TooManyListenersException {
        myIpAddress = InetAddress.getLocalHost().getHostAddress();
    }

    private static Caller createAndInitializeCaller(String username, String ipAddress, int port) throws
            PeerUnavailableException, TooManyListenersException {
        Caller classUnderTestUdp = new Caller(username, ipAddress, port, "UDP");
        classUnderTestUdp.init();
        return classUnderTestUdp;
    }

    private static Callee createAndInitializeCallee(String username, String ipAddress, int port) throws
            PeerUnavailableException, TooManyListenersException {
        Callee callee = new Callee(username, ipAddress, port, "UDP");
        callee.init();
        return callee;
    }    
}

The Registrar is siproxd

Here is a Wireshark-Dump

Ijon Tichy
  • 61
  • 9
  • It will be difficult to tell what's wrong without pcaps and debug logs here since there are 3rd parties involved. My guess is some via header is wrong or some error is not handled. Your processResponse is empty so you shouldn't expect something there. PCAPs will tell if it reaches and if malformed in any way. – Vladimir Ralev May 12 '17 at 12:25
  • I've added some more info. Also: The processResponse of my Caller isn't empty. I'm expecting the Caller to receive a response to his invite-request. – Ijon Tichy May 12 '17 at 13:43
  • From the wireshark dump it's pretty clear your siproxyd receives the ringing but doesn't forward it, so the problem is there. You proxy is misconfigured, it deletes the original Via, adds 3 branches and a record route, then it exploded the Allow header in a weird way. Can't blame the jain sip here. You probably want to start with a new proxy config to clean this up. – Vladimir Ralev May 12 '17 at 15:00

0 Answers0