jitsi / ice4j

A Java implementation of the ICE protocol
Apache License 2.0
437 stars 232 forks source link

Parsing a Session Description on Android causes a NullPointerException. #267

Open jussme opened 1 year ago

jussme commented 1 year ago

Artifact version: 3.0-61-g76e8a19

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.util.Hashtable.get(java.lang.Object)' on a null object reference
    at gov.nist.core.LexerCore.match(LexerCore.java:226)
    at gov.nist.javax.sdp.parser.OriginFieldParser.originField(OriginFieldParser.java:52)
    at gov.nist.javax.sdp.parser.OriginFieldParser.parse(OriginFieldParser.java:108)
    at gov.nist.javax.sdp.parser.SDPAnnounceParser.parse(SDPAnnounceParser.java:113)
    at org.opentelecoms.javax.sdp.NistSdpFactory.createSessionDescription(NistSdpFactory.java:62)
    at pl.mk.touchpad.server.ext.SdpUtils.parseSDP(SdpUtils.java:76)
    at pl.mk.touchpad.server.ext.RemoteConnector.pair(RemoteConnector.java:62)
    at pl.mk.touchpad.connection.net.punching.RemoteActiveConnector.connect(RemoteActiveConnector.java:66)
    at pl.mk.touchpad.connection.ControllingActivity.lambda$onCreate$0$pl-mk-touchpad-connection-ControllingActivity(ControllingActivity.java:53)
    at pl.mk.touchpad.connection.ControllingActivity$$ExternalSyntheticLambda2.run(Unknown Source:4)
    at java.lang.Thread.run(Thread.java:784)
java.text.ParseException: o=ice4j.org 0 0 IN IP4 123.12.123.123
    at gov.nist.javax.sdp.parser.OriginFieldParser.originField(OriginFieldParser.java:103)
    at gov.nist.javax.sdp.parser.OriginFieldParser.parse(OriginFieldParser.java:108)
    at gov.nist.javax.sdp.parser.SDPAnnounceParser.parse(SDPAnnounceParser.java:113)
    at org.opentelecoms.javax.sdp.NistSdpFactory.createSessionDescription(NistSdpFactory.java:62)
    at pl.mk.touchpad.server.ext.SdpUtils.parseSDP(SdpUtils.java:76)
    at pl.mk.touchpad.server.ext.RemoteConnector.pair(RemoteConnector.java:62)
    at pl.mk.touchpad.connection.net.punching.RemoteActiveConnector.connect(RemoteActiveConnector.java:66)
    at pl.mk.touchpad.connection.ControllingActivity.lambda$onCreate$0$pl-mk-touchpad-connection-ControllingActivity(ControllingActivity.java:53)
    at pl.mk.touchpad.connection.ControllingActivity$$ExternalSyntheticLambda2.run(Unknown Source:4)
    at java.lang.Thread.run(Thread.java:784)
javax.sdp.SdpParseException: o=ice4j.org 0 0 IN IP4 123.12.123.123
    at org.opentelecoms.javax.sdp.NistSdpFactory.createSessionDescription(NistSdpFactory.java:66)
    at pl.mk.touchpad.server.ext.SdpUtils.parseSDP(SdpUtils.java:76)
    at pl.mk.touchpad.server.ext.RemoteConnector.pair(RemoteConnector.java:62)
    at pl.mk.touchpad.connection.net.punching.RemoteActiveConnector.connect(RemoteActiveConnector.java:66)
    at pl.mk.touchpad.connection.ControllingActivity.lambda$onCreate$0$pl-mk-touchpad-connection-ControllingActivity(ControllingActivity.java:53)
    at pl.mk.touchpad.connection.ControllingActivity$$ExternalSyntheticLambda2.run(Unknown Source:4)
    at java.lang.Thread.run(Thread.java:784)

It seems to be thrown when calling .get() on LexerCore.currentLexer.

I've added the line currentLexer = new ConcurrentHashMap() to the constructor missing it, with Recaf. Android Studio and other decompilers confirm that it's being instantiated in both constructors (it's moved to the field declaration to be precise) but nothing changed.

Neerajsh8851 commented 1 year ago

Have you found any solution?

jussme commented 1 year ago

@Neerajsh8851 Sadly i have not, did you?

jussme commented 1 year ago

I added some printlns to LexerCore and they don't happen on Android. Also, when inspecting the fields of LexerCore, on Android with reflection, the Map fields are of type Hashtable:

protected java.util.Hashtable gov.nist.core.LexerCore.currentLexer
protected java.lang.String gov.nist.core.LexerCore.currentLexerName
protected gov.nist.core.Token gov.nist.core.LexerCore.currentMatch
public static final int gov.nist.core.LexerCore.ALPHA
static final char gov.nist.core.LexerCore.ALPHADIGIT_VALID_CHARS
static final char gov.nist.core.LexerCore.ALPHA_VALID_CHARS
public static final int gov.nist.core.LexerCore.AND
public static final int gov.nist.core.LexerCore.AT
public static final int gov.nist.core.LexerCore.BACKSLASH
public static final int gov.nist.core.LexerCore.BACK_QUOTE
public static final int gov.nist.core.LexerCore.BAR
public static final int gov.nist.core.LexerCore.COLON
public static final int gov.nist.core.LexerCore.DIGIT
static final char gov.nist.core.LexerCore.DIGIT_VALID_CHARS
public static final int gov.nist.core.LexerCore.DOLLAR
public static final int gov.nist.core.LexerCore.DOT
public static final int gov.nist.core.LexerCore.DOUBLEQUOTE
public static final int gov.nist.core.LexerCore.END
public static final int gov.nist.core.LexerCore.EQUALS
public static final int gov.nist.core.LexerCore.EXCLAMATION
public static final int gov.nist.core.LexerCore.GREATER_THAN
public static final int gov.nist.core.LexerCore.HAT
public static final int gov.nist.core.LexerCore.HT
public static final int gov.nist.core.LexerCore.ID
public static final int gov.nist.core.LexerCore.LESS_THAN
public static final int gov.nist.core.LexerCore.LPAREN
public static final int gov.nist.core.LexerCore.L_CURLY
public static final int gov.nist.core.LexerCore.L_SQUARE_BRACKET
public static final int gov.nist.core.LexerCore.MINUS
public static final int gov.nist.core.LexerCore.NULL
public static final int gov.nist.core.LexerCore.PERCENT
public static final int gov.nist.core.LexerCore.PLUS
public static final int gov.nist.core.LexerCore.POUND
public static final int gov.nist.core.LexerCore.QUESTION
public static final int gov.nist.core.LexerCore.QUOTE
public static final int gov.nist.core.LexerCore.RPAREN
public static final int gov.nist.core.LexerCore.R_CURLY
public static final int gov.nist.core.LexerCore.R_SQUARE_BRACKET
public static final int gov.nist.core.LexerCore.SAFE
public static final int gov.nist.core.LexerCore.SEMICOLON
public static final int gov.nist.core.LexerCore.SLASH
public static final int gov.nist.core.LexerCore.SP
public static final int gov.nist.core.LexerCore.STAR
public static final int gov.nist.core.LexerCore.START
public static final int gov.nist.core.LexerCore.TILDE
public static final int gov.nist.core.LexerCore.UNDERSCORE
public static final int gov.nist.core.LexerCore.WHITESPACE
protected static final java.util.Hashtable gov.nist.core.LexerCore.globalSymbolTable
protected static final java.util.Hashtable gov.nist.core.LexerCore.lexerTables

And on desktop, notice the dummyField field i added:

public static final int gov.nist.core.LexerCore.START
public static final int gov.nist.core.LexerCore.END
public static final int gov.nist.core.LexerCore.ID_NO_WHITESPACE
public static final int gov.nist.core.LexerCore.ID
public static final int gov.nist.core.LexerCore.SAFE
public static final int gov.nist.core.LexerCore.WHITESPACE
public static final int gov.nist.core.LexerCore.DIGIT
public static final int gov.nist.core.LexerCore.ALPHA
public static final int gov.nist.core.LexerCore.BACKSLASH
public static final int gov.nist.core.LexerCore.QUOTE
public static final int gov.nist.core.LexerCore.AT
public static final int gov.nist.core.LexerCore.SP
public static final int gov.nist.core.LexerCore.HT
public static final int gov.nist.core.LexerCore.COLON
public static final int gov.nist.core.LexerCore.STAR
public static final int gov.nist.core.LexerCore.DOLLAR
public static final int gov.nist.core.LexerCore.PLUS
public static final int gov.nist.core.LexerCore.POUND
public static final int gov.nist.core.LexerCore.MINUS
public static final int gov.nist.core.LexerCore.DOUBLEQUOTE
public static final int gov.nist.core.LexerCore.TILDE
public static final int gov.nist.core.LexerCore.BACK_QUOTE
public static final int gov.nist.core.LexerCore.NULL
public static final int gov.nist.core.LexerCore.EQUALS
public static final int gov.nist.core.LexerCore.SEMICOLON
public static final int gov.nist.core.LexerCore.SLASH
public static final int gov.nist.core.LexerCore.L_SQUARE_BRACKET
public static final int gov.nist.core.LexerCore.R_SQUARE_BRACKET
public static final int gov.nist.core.LexerCore.R_CURLY
public static final int gov.nist.core.LexerCore.L_CURLY
public static final int gov.nist.core.LexerCore.HAT
public static final int gov.nist.core.LexerCore.BAR
public static final int gov.nist.core.LexerCore.DOT
public static final int gov.nist.core.LexerCore.EXCLAMATION
public static final int gov.nist.core.LexerCore.LPAREN
public static final int gov.nist.core.LexerCore.RPAREN
public static final int gov.nist.core.LexerCore.GREATER_THAN
public static final int gov.nist.core.LexerCore.LESS_THAN
public static final int gov.nist.core.LexerCore.PERCENT
public static final int gov.nist.core.LexerCore.QUESTION
public static final int gov.nist.core.LexerCore.AND
public static final int gov.nist.core.LexerCore.UNDERSCORE
protected static final java.util.concurrent.ConcurrentHashMap gov.nist.core.LexerCore.globalSymbolTable
protected static final java.util.concurrent.ConcurrentHashMap gov.nist.core.LexerCore.lexerTables
protected java.util.Map gov.nist.core.LexerCore.currentLexer
protected java.lang.String gov.nist.core.LexerCore.dummyField
protected java.lang.String gov.nist.core.LexerCore.currentLexerName
protected gov.nist.core.Token gov.nist.core.LexerCore.currentMatch
static final char gov.nist.core.LexerCore.ALPHA_VALID_CHARS
static final char gov.nist.core.LexerCore.DIGIT_VALID_CHARS
static final char gov.nist.core.LexerCore.ALPHADIGIT_VALID_CHARS

This is from the same artifact on Android and Desktop. Also, the lines executed seen in Android debugger don't match the source file. Changes made to LexerCore don't seem to effect the artifact. What is going on here? I don't see reflection being used in regards to LexerCore. Why is LexerCore on runtime different from the source code?

jussme commented 1 year ago

Also, a println added to the class static block is not executed.

Neerajsh8851 commented 1 year ago

@Neerajsh8851 Sadly i have not, did you?

I am creating my own solution in kotlin. I found pj project but that was in c.

davidliu commented 1 year ago

@jussme Did a little digging, and the issue is because LexerCore appears to conflict with a hidden embedded framework class. Looking at the LexerCore class with a debugger, you can find a dexCache field which I believe indicates the location the class was loaded from. Other app classes have a path consistent with coming from the apk's dex files, but LexerCore gives me a path /system/framework/voip-common.jar. It looks like they have an internal copy of the NIST classes. Not sure how to workaround this issue; will need an android specific build like https://mvnrepository.com/artifact/javax.sip/android-jain-sip-ri which changes the package names so this conflict doesn't happen.

davidliu commented 1 year ago

Not sure if it will work for Ice4J as well, but you can try shadowing the jars to avoid the class name conflict. This can be done fairly simply with a shadow plugin like https://imperceptiblethoughts.com/shadow/.

I'm using this on java-sdp-nist-bridge, which includes the jain-sip library. For simplicity, I just have a separate gradle module that has all the dependencies I want to shadow, and then I use the auto-relocation feature to prefix everything, and depend on the shadow module from my app module.