diff --git a/code-generation/language/java/src/main/resources/templates/java/complex-type-template.java.ftlh b/code-generation/language/java/src/main/resources/templates/java/complex-type-template.java.ftlh index efd4444ae0..37d461a178 100644 --- a/code-generation/language/java/src/main/resources/templates/java/complex-type-template.java.ftlh +++ b/code-generation/language/java/src/main/resources/templates/java/complex-type-template.java.ftlh @@ -60,6 +60,7 @@ import org.apache.plc4x.java.api.exceptions.*; import org.apache.plc4x.java.spi.generation.*; import org.apache.plc4x.java.api.value.*; +import java.io.*; import java.time.*; import java.util.*; import java.math.BigInteger; @@ -623,8 +624,15 @@ ${helper.getExternalTypeImports()} <#assign arrayElementTypeReference = arrayField.type.asArrayTypeReference().orElseThrow().getElementTypeReference()> <#if arrayElementTypeReference.isByteBased()> - <#if !field.isCountArrayField() && !field.isLengthArrayField()>${helper.fail("array fields of type byte only support 'count' and 'length' loop-types.")} - byte[] ${namedField.name} = readBuffer.readByteArray("${namedField.name}", Math.toIntExact(${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)}); + <#if field.isCountArrayField() || field.isLengthArrayField()> + byte[] ${namedField.name} = readBuffer.readByteArray("${namedField.name}", Math.toIntExact(${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)}); + <#else> + ByteArrayOutputStream ${namedField.name}Buffer = new ByteArrayOutputStream(); + while (${helper.toParseExpression(arrayField, helper.boolTypeReference, arrayField.loopExpression, parserArguments)}) { + ${namedField.name}Buffer.write(readBuffer.readByte("${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)})); + } + byte[] ${namedField.name} = ${namedField.name}Buffer.toByteArray(); + <#else> <#-- If this is a count array, we can directly initialize an array with the given size --> <#if field.isCountArrayField()> diff --git a/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4 b/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4 index 3427650db8..f490096c31 100644 --- a/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4 +++ b/code-generation/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4 @@ -395,8 +395,13 @@ BOOLEAN_LITERAL STRING_LITERAL : '"' STRING_CHARACTERS? '"' + | SINGLE_LINE_STRING + | MULTI_LINE_STRING ; +SINGLE_LINE_STRING : '"' ( ESCAPE | '\\"' | ~[\\"])* '"'; +MULTI_LINE_STRING : '"""' .*? '"""'; + // As we're generating property names and class names from these, // we have to put more restrictions on them. @@ -404,6 +409,8 @@ IDENTIFIER_LITERAL : [A-Za-z0-9_-]+ ; +fragment ESCAPE: '\\' ( [\\abdefnrstv0] | 'x' HEX_LITERAL HEX_LITERAL | 'u' HEX_LITERAL HEX_LITERAL HEX_LITERAL HEX_LITERAL | 'u{' HEX_LITERAL+ '}'); + fragment STRING_CHARACTERS : STRING_CHARACTER+ diff --git a/plc4j/drivers/pom.xml b/plc4j/drivers/pom.xml index deb6a14ca0..6774b493f6 100644 --- a/plc4j/drivers/pom.xml +++ b/plc4j/drivers/pom.xml @@ -57,6 +57,7 @@ profinet profinet-ng s7 + sip simulated all diff --git a/plc4j/drivers/sip/pom.xml b/plc4j/drivers/sip/pom.xml new file mode 100644 index 0000000000..2e51565c54 --- /dev/null +++ b/plc4j/drivers/sip/pom.xml @@ -0,0 +1,238 @@ + + + + 4.0.0 + + + org.apache.plc4x + plc4j-drivers + 0.14.0-SNAPSHOT + + + plc4j-driver-sip + + PLC4J: Driver: SIP + Implementation of a PLC4X driver for the SIP protocol. + + + 2025-08-02T13:55:11Z + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + generate-feature-xml + compile + + + features-generate-descriptor + + verify + + + true + true + + + + build-kar + package + + + kar + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.groupId}.${project.artifactId} + org.apache.plc4x.java.osgi.DriverActivator + org.apache.plc4x.java.api.PlcDriver,org.apache.plc4x.java.sip.readwrite.sipDriver + + + * + + * + + + + + + + + + org.apache.plc4x + plc4j-api + 0.14.0-SNAPSHOT + + + org.apache.plc4x + plc4j-spi + 0.14.0-SNAPSHOT + + + + org.apache.plc4x + plc4j-transport-udp + 0.14.0-SNAPSHOT + + + + org.pcap4j + pcap4j-core + + compile + + + org.pcap4j + pcap4j-packetfactory-static + runtime + + + + org.apache.commons + commons-lang3 + + + commons-codec + commons-codec + + + io.netty + netty-buffer + + + io.netty + netty-transport + + + io.netty + netty-handler + + + io.netty + netty-codec + + + io.netty + netty-common + + + org.json + json + + + + org.apache.plc4x + plc4j-utils-test-utils + 0.14.0-SNAPSHOT + test + + + org.skyscreamer + jsonassert + test + + + com.fasterxml.jackson.core + jackson-annotations + test + + + + org.apache.plc4x + plc4x-protocols-sip + 0.14.0-SNAPSHOT + tests + test-jar + test + + + + + + update-generated-code + + + + org.apache.plc4x.plugins + plc4x-maven-plugin + + + generate-driver + generate-sources + + generate-driver + + + sip + java + read-write + src/main/generated + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.plc4x:plc4x-code-generation-language-java + org.apache.plc4x:plc4x-protocols-sip + + + + + + + + + org.apache.plc4x + plc4x-code-generation-language-java + 0.14.0-SNAPSHOT + + provided + + + + org.apache.plc4x + plc4x-protocols-sip + 0.14.0-SNAPSHOT + + provided + + + + + + diff --git a/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/Constants.java b/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/Constants.java new file mode 100644 index 0000000000..a8e74b7992 --- /dev/null +++ b/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/Constants.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.sip.readwrite; + +import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*; +import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*; +import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*; +import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*; +import static org.apache.plc4x.java.spi.generation.StaticHelper.*; + +import java.io.*; +import java.time.*; +import java.util.*; +import org.apache.plc4x.java.api.exceptions.*; +import org.apache.plc4x.java.api.value.*; +import org.apache.plc4x.java.spi.codegen.*; +import org.apache.plc4x.java.spi.codegen.fields.*; +import org.apache.plc4x.java.spi.codegen.io.*; +import org.apache.plc4x.java.spi.generation.*; + +// Code generated by code-generation. DO NOT EDIT. + +public class Constants implements Message { + + // Constant values. + public static final Byte SPACE = 0x20; + public static final Byte COLON = 0x3a; + public static final Byte SLASH = 0x2f; + public static final Byte R = 0x0d; + public static final Byte N = 0x0a; + + public Constants() { + super(); + } + + public byte getSPACE() { + return SPACE; + } + + public byte getCOLON() { + return COLON; + } + + public byte getSLASH() { + return SLASH; + } + + public byte getR() { + return R; + } + + public byte getN() { + return N; + } + + public void serialize(WriteBuffer writeBuffer) throws SerializationException { + PositionAware positionAware = writeBuffer; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + writeBuffer.pushContext("Constants"); + + // Const Field (SPACE) + writeConstField("SPACE", SPACE, writeByte(writeBuffer, 8)); + + // Const Field (COLON) + writeConstField("COLON", COLON, writeByte(writeBuffer, 8)); + + // Const Field (SLASH) + writeConstField("SLASH", SLASH, writeByte(writeBuffer, 8)); + + // Const Field (R) + writeConstField("R", R, writeByte(writeBuffer, 8)); + + // Const Field (N) + writeConstField("N", N, writeByte(writeBuffer, 8)); + + writeBuffer.popContext("Constants"); + } + + @Override + public int getLengthInBytes() { + return (int) Math.ceil((float) getLengthInBits() / 8.0); + } + + @Override + public int getLengthInBits() { + int lengthInBits = 0; + Constants _value = this; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + + // Const Field (SPACE) + lengthInBits += 8; + + // Const Field (COLON) + lengthInBits += 8; + + // Const Field (SLASH) + lengthInBits += 8; + + // Const Field (R) + lengthInBits += 8; + + // Const Field (N) + lengthInBits += 8; + + return lengthInBits; + } + + public static Constants staticParse(ReadBuffer readBuffer) throws ParseException { + readBuffer.pullContext("Constants"); + PositionAware positionAware = readBuffer; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + + byte SPACE = readConstField("SPACE", readByte(readBuffer, 8), Constants.SPACE); + + byte COLON = readConstField("COLON", readByte(readBuffer, 8), Constants.COLON); + + byte SLASH = readConstField("SLASH", readByte(readBuffer, 8), Constants.SLASH); + + byte R = readConstField("R", readByte(readBuffer, 8), Constants.R); + + byte N = readConstField("N", readByte(readBuffer, 8), Constants.N); + + readBuffer.closeContext("Constants"); + // Create the instance + Constants _constants; + _constants = new Constants(); + return _constants; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Constants)) { + return false; + } + Constants that = (Constants) o; + return true; + } + + @Override + public int hashCode() { + return Objects.hash(); + } + + @Override + public String toString() { + WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true); + try { + writeBufferBoxBased.writeSerializable(this); + } catch (SerializationException e) { + throw new RuntimeException(e); + } + return "\n" + writeBufferBoxBased.getBox().toString() + "\n"; + } +} diff --git a/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/SipPDU.java b/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/SipPDU.java new file mode 100644 index 0000000000..2ae9375f63 --- /dev/null +++ b/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/SipPDU.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.sip.readwrite; + +import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*; +import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*; +import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*; +import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*; +import static org.apache.plc4x.java.spi.generation.StaticHelper.*; + +import java.io.*; +import java.time.*; +import java.util.*; +import org.apache.plc4x.java.api.exceptions.*; +import org.apache.plc4x.java.api.value.*; +import org.apache.plc4x.java.spi.codegen.*; +import org.apache.plc4x.java.spi.codegen.fields.*; +import org.apache.plc4x.java.spi.codegen.io.*; +import org.apache.plc4x.java.spi.generation.*; + +// Code generated by code-generation. DO NOT EDIT. + +public class SipPDU implements Message { + + // Properties. + protected final SipRequestLine requestLine; + protected final List
headers; + protected final byte[] payload; + + public SipPDU(SipRequestLine requestLine, List
headers, byte[] payload) { + super(); + this.requestLine = requestLine; + this.headers = headers; + this.payload = payload; + } + + public SipRequestLine getRequestLine() { + return requestLine; + } + + public List
getHeaders() { + return headers; + } + + public byte[] getPayload() { + return payload; + } + + public void serialize(WriteBuffer writeBuffer) throws SerializationException { + PositionAware positionAware = writeBuffer; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + writeBuffer.pushContext("SipPDU"); + + // Simple Field (requestLine) + writeSimpleField( + "requestLine", + requestLine, + writeComplex(writeBuffer), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + // Array Field (headers) + writeComplexTypeArrayField( + "headers", headers, writeBuffer, WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + // Array Field (payload) + writeByteArrayField( + "payload", + payload, + writeByteArray(writeBuffer, 8), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + // Reserved Field (reserved) + writeReservedField( + "reserved", + (byte) Constants.R, + writeByte(writeBuffer, 8), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + // Reserved Field (reserved) + writeReservedField( + "reserved", + (byte) Constants.N, + writeByte(writeBuffer, 8), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + writeBuffer.popContext("SipPDU"); + } + + @Override + public int getLengthInBytes() { + return (int) Math.ceil((float) getLengthInBits() / 8.0); + } + + @Override + public int getLengthInBits() { + int lengthInBits = 0; + SipPDU _value = this; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + + // Simple field (requestLine) + lengthInBits += requestLine.getLengthInBits(); + + // Array field + if (headers != null) { + for (Message element : headers) { + lengthInBits += element.getLengthInBits(); + } + } + + // Array field + if (payload != null) { + lengthInBits += 8 * payload.length; + } + + // Reserved Field (reserved) + lengthInBits += 8; + + // Reserved Field (reserved) + lengthInBits += 8; + + return lengthInBits; + } + + public static SipPDU staticParse(ReadBuffer readBuffer, Integer len) throws ParseException { + readBuffer.pullContext("SipPDU"); + PositionAware positionAware = readBuffer; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + + SipRequestLine requestLine = + readSimpleField( + "requestLine", + readComplex(() -> SipRequestLine.staticParse(readBuffer), readBuffer), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + List
headers = + readLengthArrayField( + "headers", + readComplex(() -> Header.staticParse(readBuffer), readBuffer), + org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.untilToken( + readBuffer, "\r\n\r\n", 2), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + byte[] payload = + readBuffer.readByteArray( + "payload", + Math.toIntExact( + (((len) - (requestLine.getLengthInBytes())) - (ARRAY_SIZE_IN_BYTES(headers))) + - (2)), + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + Byte reservedField0 = + readReservedField( + "reserved", + readByte(readBuffer, 8), + (byte) Constants.R, + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + Byte reservedField1 = + readReservedField( + "reserved", + readByte(readBuffer, 8), + (byte) Constants.N, + WithOption.WithByteOrder(ByteOrder.BIG_ENDIAN)); + + readBuffer.closeContext("SipPDU"); + // Create the instance + SipPDU _sipPDU; + _sipPDU = new SipPDU(requestLine, headers, payload); + return _sipPDU; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SipPDU)) { + return false; + } + SipPDU that = (SipPDU) o; + return (getRequestLine() == that.getRequestLine()) + && (getHeaders() == that.getHeaders()) + && (getPayload() == that.getPayload()) + && true; + } + + @Override + public int hashCode() { + return Objects.hash(getRequestLine(), getHeaders(), getPayload()); + } + + @Override + public String toString() { + WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true); + try { + writeBufferBoxBased.writeSerializable(this); + } catch (SerializationException e) { + throw new RuntimeException(e); + } + return "\n" + writeBufferBoxBased.getBox().toString() + "\n"; + } +} diff --git a/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/SipRequestLine.java b/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/SipRequestLine.java new file mode 100644 index 0000000000..622e6d97cd --- /dev/null +++ b/plc4j/drivers/sip/src/main/generated/org/apache/plc4x/java/sip/readwrite/SipRequestLine.java @@ -0,0 +1,298 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.sip.readwrite; + +import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*; +import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*; +import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*; +import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*; +import static org.apache.plc4x.java.spi.generation.StaticHelper.*; + +import java.io.*; +import java.time.*; +import java.util.*; +import org.apache.plc4x.java.api.exceptions.*; +import org.apache.plc4x.java.api.value.*; +import org.apache.plc4x.java.spi.codegen.*; +import org.apache.plc4x.java.spi.codegen.fields.*; +import org.apache.plc4x.java.spi.codegen.io.*; +import org.apache.plc4x.java.spi.generation.*; + +// Code generated by code-generation. DO NOT EDIT. + +public class SipRequestLine implements Message { + + // Properties. + protected final String method; + protected final String proto; + protected final String requestUri; + protected final String protocol; + protected final String version; + + public SipRequestLine( + String method, String proto, String requestUri, String protocol, String version) { + super(); + this.method = method; + this.proto = proto; + this.requestUri = requestUri; + this.protocol = protocol; + this.version = version; + } + + public String getMethod() { + return method; + } + + public String getProto() { + return proto; + } + + public String getRequestUri() { + return requestUri; + } + + public String getProtocol() { + return protocol; + } + + public String getVersion() { + return version; + } + + public void serialize(WriteBuffer writeBuffer) throws SerializationException { + PositionAware positionAware = writeBuffer; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + writeBuffer.pushContext("SipRequestLine"); + + // Manual Field (method) + writeManualField( + "method", + () -> + org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.writeStringTill( + writeBuffer, method), + writeBuffer); + + // Reserved Field (reserved) + writeReservedField("reserved", (byte) Constants.SPACE, writeByte(writeBuffer, 8)); + + // Manual Field (proto) + writeManualField( + "proto", + () -> + org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.writeStringTill( + writeBuffer, proto), + writeBuffer); + + // Reserved Field (reserved) + writeReservedField("reserved", (byte) Constants.COLON, writeByte(writeBuffer, 8)); + + // Manual Field (requestUri) + writeManualField( + "requestUri", + () -> + org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.writeStringTill( + writeBuffer, requestUri), + writeBuffer); + + // Reserved Field (reserved) + writeReservedField("reserved", (byte) Constants.SPACE, writeByte(writeBuffer, 8)); + + // Manual Field (protocol) + writeManualField( + "protocol", + () -> + org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.writeStringTill( + writeBuffer, protocol), + writeBuffer); + + // Reserved Field (reserved) + writeReservedField("reserved", (byte) Constants.SLASH, writeByte(writeBuffer, 8)); + + // Manual Field (version) + writeManualField( + "version", + () -> + org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.writeStringTill( + writeBuffer, version), + writeBuffer); + + // Reserved Field (reserved) + writeReservedField("reserved", (byte) Constants.R, writeByte(writeBuffer, 8)); + + // Reserved Field (reserved) + writeReservedField("reserved", (byte) Constants.N, writeByte(writeBuffer, 8)); + + writeBuffer.popContext("SipRequestLine"); + } + + @Override + public int getLengthInBytes() { + return (int) Math.ceil((float) getLengthInBits() / 8.0); + } + + @Override + public int getLengthInBits() { + int lengthInBits = 0; + SipRequestLine _value = this; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + + // Manual Field (method) + lengthInBits += (8) * (STR_LEN(method)); + + // Reserved Field (reserved) + lengthInBits += 8; + + // Manual Field (proto) + lengthInBits += (8) * (STR_LEN(proto)); + + // Reserved Field (reserved) + lengthInBits += 8; + + // Manual Field (requestUri) + lengthInBits += (8) * (STR_LEN(requestUri)); + + // Reserved Field (reserved) + lengthInBits += 8; + + // Manual Field (protocol) + lengthInBits += (8) * (STR_LEN(protocol)); + + // Reserved Field (reserved) + lengthInBits += 8; + + // Manual Field (version) + lengthInBits += (8) * (STR_LEN(version)); + + // Reserved Field (reserved) + lengthInBits += 8; + + // Reserved Field (reserved) + lengthInBits += 8; + + return lengthInBits; + } + + public static SipRequestLine staticParse(ReadBuffer readBuffer) throws ParseException { + readBuffer.pullContext("SipRequestLine"); + PositionAware positionAware = readBuffer; + boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get(); + + String method = + readManualField( + "method", + readBuffer, + () -> + (String) + (org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.readStringTill( + readBuffer, " "))); + + Byte reservedField0 = + readReservedField("reserved", readByte(readBuffer, 8), (byte) Constants.SPACE); + + String proto = + readManualField( + "proto", + readBuffer, + () -> + (String) + (org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.readStringTill( + readBuffer, ":"))); + + Byte reservedField1 = + readReservedField("reserved", readByte(readBuffer, 8), (byte) Constants.COLON); + + String requestUri = + readManualField( + "requestUri", + readBuffer, + () -> + (String) + (org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.readStringTill( + readBuffer, " "))); + + Byte reservedField2 = + readReservedField("reserved", readByte(readBuffer, 8), (byte) Constants.SPACE); + + String protocol = + readManualField( + "protocol", + readBuffer, + () -> + (String) + (org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.readStringTill( + readBuffer, "/"))); + + Byte reservedField3 = + readReservedField("reserved", readByte(readBuffer, 8), (byte) Constants.SLASH); + + String version = + readManualField( + "version", + readBuffer, + () -> + (String) + (org.apache.plc4x.java.sip.readwrite.utils.StaticHelper.readStringTill( + readBuffer, "\r\n"))); + + Byte reservedField4 = + readReservedField("reserved", readByte(readBuffer, 8), (byte) Constants.R); + + Byte reservedField5 = + readReservedField("reserved", readByte(readBuffer, 8), (byte) Constants.N); + + readBuffer.closeContext("SipRequestLine"); + // Create the instance + SipRequestLine _sipRequestLine; + _sipRequestLine = new SipRequestLine(method, proto, requestUri, protocol, version); + return _sipRequestLine; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SipRequestLine)) { + return false; + } + SipRequestLine that = (SipRequestLine) o; + return (getMethod() == that.getMethod()) + && (getProto() == that.getProto()) + && (getRequestUri() == that.getRequestUri()) + && (getProtocol() == that.getProtocol()) + && (getVersion() == that.getVersion()) + && true; + } + + @Override + public int hashCode() { + return Objects.hash(getMethod(), getProto(), getRequestUri(), getProtocol(), getVersion()); + } + + @Override + public String toString() { + WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true); + try { + writeBufferBoxBased.writeSerializable(this); + } catch (SerializationException e) { + throw new RuntimeException(e); + } + return "\n" + writeBufferBoxBased.getBox().toString() + "\n"; + } +} diff --git a/plc4j/drivers/sip/src/main/java/org/apache/plc4x/java/sip/readwrite/utils/StaticHelper.java b/plc4j/drivers/sip/src/main/java/org/apache/plc4x/java/sip/readwrite/utils/StaticHelper.java new file mode 100644 index 0000000000..409487a691 --- /dev/null +++ b/plc4j/drivers/sip/src/main/java/org/apache/plc4x/java/sip/readwrite/utils/StaticHelper.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.sip.readwrite.utils; + +import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; +import org.apache.plc4x.java.spi.generation.ParseException; +import org.apache.plc4x.java.spi.generation.ReadBuffer; +import org.apache.plc4x.java.spi.generation.SerializationException; +import org.apache.plc4x.java.spi.generation.WriteBuffer; + +public class StaticHelper { + + public static String asString(byte[] bytes) { + return new String(bytes); + } + + public static int untilToken(ReadBuffer readBuffer, String terminator, int keep) { + int start = readBuffer.getPos(); + StringBuilder buffer = new StringBuilder(); + int length = terminator.length(); + int retrieved = 0; + boolean success = false; + while (readBuffer.hasMore(8) && retrieved < length) { + try { + buffer.append((char) readBuffer.readByte()); + if (buffer.length() >= length) { + if (buffer.toString().endsWith(terminator)) { + success = true; + break; + } + } + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + if (!success) { + throw new PlcRuntimeException("Failed to reach termination sequence for array"); + } + + int end = readBuffer.getPos(); + readBuffer.reset(start); + return end - start - keep; + } + + public static boolean until(ReadBuffer readBuffer, String terminator) { + int start = readBuffer.getPos(); + StringBuilder buffer = new StringBuilder(); + int length = terminator.length(); + int retrieved = 0; + while (readBuffer.hasMore(8) && retrieved < length) { + try { + buffer.append((char) readBuffer.readByte()); + if (buffer.length() == length) { + if (buffer.toString().equals(terminator)) { + readBuffer.reset(start + length - 1); + return false; + } + break; + } + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + readBuffer.reset(start); + return true; + } + + public static String readStringTill(ReadBuffer readBuffer, String terminator) throws ParseException { + int start = readBuffer.getPos(); + StringBuilder buffer = new StringBuilder(); + int length = terminator.length(); + while (readBuffer.hasMore(8)) { + buffer.append((char) readBuffer.readByte()); + if (buffer.length() >= length) { + if (buffer.subSequence(buffer.length() - length, buffer.length()).equals(terminator)) { + break; + } + } + } + + int end = readBuffer.getPos(); + readBuffer.reset(end - length); + if (start == end) { + return ""; + } + return buffer.subSequence(0, buffer.length() - length).toString(); + } + + public static String readString(ReadBuffer readBuffer, String terminator) throws ParseException { + int start = readBuffer.getPos(); + StringBuilder buffer = new StringBuilder(); + int length = terminator.length(); + while (readBuffer.hasMore(8)) { + buffer.append((char) readBuffer.readByte()); + if (buffer.length() >= length) { + if (buffer.subSequence(buffer.length() - length, buffer.length()).equals(terminator)) { + break; + } + } + } + + int end = readBuffer.getPos(); + //readBuffer.reset(end - length); + if (start == end) { + return ""; + } + //return buffer.subSequence(0, buffer.length() - length).toString(); + return buffer.toString(); + } + + public static void writeString(WriteBuffer writeBuffer, String method/*, String terminator*/) throws SerializationException { + writeBuffer.writeString(8 * method.length()/* + (terminator.length() * 8)*/ , method/* + terminator*/); + } + + public static void writeStringTill(WriteBuffer writeBuffer, String method) throws SerializationException { + writeBuffer.writeString(8 * method.length(), method); + } +} diff --git a/plc4j/drivers/sip/src/test/java/org/apache/plc4x/java/sip/readwrite/SipParserSerializerTest.java b/plc4j/drivers/sip/src/test/java/org/apache/plc4x/java/sip/readwrite/SipParserSerializerTest.java new file mode 100644 index 0000000000..387a286cb0 --- /dev/null +++ b/plc4j/drivers/sip/src/test/java/org/apache/plc4x/java/sip/readwrite/SipParserSerializerTest.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.sip.readwrite; + +import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner; + +public class SipParserSerializerTest extends ParserSerializerTestsuiteRunner { + + public SipParserSerializerTest() { + super("/protocols/sip/ParserSerializerTestsuite.xml"); + } + +} diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java index 47e3ab016c..84ec9aab06 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java @@ -204,8 +204,8 @@ public void writeBigDecimal(String logicalName, int bitLength, BigDecimal value, @Override public void writeString(String logicalName, int bitLength, String value, WithWriterArgs... writerArgs) throws SerializationException { String encoding = extractEncoding(writerArgs).orElse("UTF-8"); - String cleanedUpString = StringUtils.trimToEmpty(value).replaceAll("[^\u0009\r\n\u0020-\uD7FF\uE000-\uFFFD\ud800\udc00-\udbff\udfff]", ""); - createAndAppend(logicalName, rwStringKey, bitLength, cleanedUpString, encoding, writerArgs); + //String cleanedUpString = StringUtils.trimToEmpty(value).replaceAll("[^\u0009\r\n\u0020-\uD7FF\uE000-\uFFFD\ud800\udc00-\udbff\udfff]", ""); + createAndAppend(logicalName, rwStringKey, bitLength, value, encoding, writerArgs); move(bitLength); } diff --git a/protocols/pom.xml b/protocols/pom.xml index 6baa7e1c28..6d802989e7 100644 --- a/protocols/pom.xml +++ b/protocols/pom.xml @@ -58,6 +58,7 @@ profinet s7 simulated + sip socketcan umas diff --git a/protocols/sip/pom.xml b/protocols/sip/pom.xml new file mode 100644 index 0000000000..5fef82e40f --- /dev/null +++ b/protocols/sip/pom.xml @@ -0,0 +1,53 @@ + + + + + 4.0.0 + + + org.apache.plc4x + plc4x-protocols + 0.14.0-SNAPSHOT + + + plc4x-protocols-sip + + Protocols: SIP + Base protocol specifications for the SIP protocol + + + 2025-08-02T13:55:11Z + + + + + org.apache.plc4x + plc4x-code-generation-protocol-base-mspec + 0.14.0-SNAPSHOT + + + + ch.qos.logback + logback-classic + test + + + + \ No newline at end of file diff --git a/protocols/sip/src/main/java/org/apache/plc4x/protocol/sip/SipProtocol.java b/protocols/sip/src/main/java/org/apache/plc4x/protocol/sip/SipProtocol.java new file mode 100644 index 0000000000..40fc89e99f --- /dev/null +++ b/protocols/sip/src/main/java/org/apache/plc4x/protocol/sip/SipProtocol.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.protocol.sip; + +import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser; +import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers; +import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext; +import org.apache.plc4x.plugins.codegenerator.protocol.Protocol; +import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext; +import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException; + +public class SipProtocol implements Protocol, ProtocolHelpers { + + @Override + public String getName() { + return "sip"; + } + + @Override + public TypeContext getTypeContext() throws GenerationException { + ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream()); + typeContext.validate(); + return typeContext; + } + +} diff --git a/protocols/sip/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol b/protocols/sip/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol new file mode 100644 index 0000000000..3a2e7a3183 --- /dev/null +++ b/protocols/sip/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +org.apache.plc4x.protocol.sip.SipProtocol diff --git a/protocols/sip/src/main/resources/protocols/sip/sip.mspec b/protocols/sip/src/main/resources/protocols/sip/sip.mspec new file mode 100644 index 0000000000..4945894fa5 --- /dev/null +++ b/protocols/sip/src/main/resources/protocols/sip/sip.mspec @@ -0,0 +1,40 @@ +[type Constants + [const byte SPACE 0x20] + [const byte COLON 0x3a] + [const byte SLASH 0x2f] + [const byte R 0x0d] + [const byte N 0x0a] +] + +[type SipPDU (uint 16 len) byteOrder='BIG_ENDIAN' + [simple SipRequestLine requestLine] + [array Header headers length 'STATIC_CALL("untilToken", readBuffer, "\r\n\r\n", 2)'] + [array byte payload length 'len - requestLine.lengthInBytes - ARRAY_SIZE_IN_BYTES(headers) - 2'] + [reserved byte 8 'Constants.R'] + [reserved byte 8 'Constants.N'] + +] + +[type Header + [manual vstring header 'STATIC_CALL("readStringTill", readBuffer, ":")' 'STATIC_CALL("writeStringTill", writeBuffer, header)' '8 * STR_LEN(header)' ] + [reserved byte 8 'Constants.COLON'] + [reserved byte 8 'Constants.SPACE'] + [manual vstring value 'STATIC_CALL("readStringTill", readBuffer, "\r\n")' 'STATIC_CALL("writeStringTill", writeBuffer, value)' '8 * STR_LEN(value)' ] + [const byte R 0x0d] + [const byte N 0x0a] +] + +[type SipRequestLine + [manual vstring method 'STATIC_CALL("readStringTill", readBuffer, " ")' 'STATIC_CALL("writeStringTill", writeBuffer, method)' '8 * STR_LEN(method)' ] + [reserved byte 8 'Constants.SPACE'] + [manual vstring proto 'STATIC_CALL("readStringTill", readBuffer, ":")' 'STATIC_CALL("writeStringTill", writeBuffer, proto)' '8 * STR_LEN(proto)' ] + [reserved byte 8 'Constants.COLON'] + [manual vstring requestUri 'STATIC_CALL("readStringTill", readBuffer, " ")' 'STATIC_CALL("writeStringTill", writeBuffer, requestUri)' '8 * STR_LEN(requestUri)' ] + [reserved byte 8 'Constants.SPACE'] + [manual vstring protocol 'STATIC_CALL("readStringTill", readBuffer, "/")' 'STATIC_CALL("writeStringTill", writeBuffer, protocol)' '8 * STR_LEN(protocol)' ] + [reserved byte 8 'Constants.SLASH'] + [manual vstring version 'STATIC_CALL("readStringTill", readBuffer, "\r\n")' 'STATIC_CALL("writeStringTill", writeBuffer, version)' '8 * STR_LEN(version)' ] + [reserved byte 8 'Constants.R'] + [reserved byte 8 'Constants.N'] +] + diff --git a/protocols/sip/src/test/resources/protocols/sip/ParserSerializerTestsuite.xml b/protocols/sip/src/test/resources/protocols/sip/ParserSerializerTestsuite.xml new file mode 100644 index 0000000000..bf6667a8cb --- /dev/null +++ b/protocols/sip/src/test/resources/protocols/sip/ParserSerializerTestsuite.xml @@ -0,0 +1,294 @@ + + + + + SIP + + sip + read-write + + + SIP Register + 5245474953544552207369703a7369702e6379626572636974792e646b205349502f322e300d0a5669613a205349502f322e302f554450203139322e3136382e312e323b6272616e63683d7a39684734624b6e703135313234383733372d34366561373135653139322e3136382e312e323b72706f72740d0a46726f6d3a203c7369703a766f693138303633407369702e6379626572636974792e646b3e3b7461673d393033646630610d0a546f3a203c7369703a766f693138303633407369702e6379626572636974792e646b3e0d0a43616c6c2d49443a203537383232323732392d3436363564373735403537383232323733322d34363635643737320d0a436f6e746163743a20203c7369703a766f693138303633403139322e3136382e312e323a353036303b6c696e653d396337643264626438383232303133633e3b657870697265733d313230303b713d302e3530300d0a457870697265733a20313230300d0a435365713a2036382052454749535445520d0a436f6e74656e742d4c656e6774683a20300d0a4d61782d466f7277617264733a2037300d0a557365722d4167656e743a204e65726f2053495050532049502050686f6e652056657273696f6e20322e302e35312e31360d0a0d0a + SipPDU + + 467 + + + + + + REGISTER + 0x20 + sip + 0x3a + sip.cybercity.dk + 0x20 + SIP + 0x2f + 2.0 + 0x0d + 0x0a + + + +
+ Via + 0x3a + 0x20 + SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp151248737-46ea715e192.168.1.2;rport + 0x0d + 0x0a +
+
+ From + 0x3a + 0x20 + <sip:voi18063@sip.cybercity.dk>;tag=903df0a + 0x0d + 0x0a +
+
+ To + 0x3a + 0x20 + <sip:voi18063@sip.cybercity.dk> + 0x0d + 0x0a +
+
+ Call-ID + 0x3a + 0x20 + 578222729-4665d775@578222732-4665d772 + 0x0d + 0x0a +
+
+ Contact + 0x3a + 0x20 + <sip:voi18063@192.168.1.2:5060;line=9c7d2dbd8822013c>;expires=1200;q=0.500 + 0x0d + 0x0a +
+
+ Expires + 0x3a + 0x20 + 1200 + 0x0d + 0x0a +
+
+ CSeq + 0x3a + 0x20 + 68 REGISTER + 0x0d + 0x0a +
+
+ Content-Length + 0x3a + 0x20 + 0 + 0x0d + 0x0a +
+
+ Max-Forwards + 0x3a + 0x20 + 70 + 0x0d + 0x0a +
+
+ User-Agent + 0x3a + 0x20 + Nero SIPPS IP Phone Version 2.0.51.16 + 0x0d + 0x0a +
+
+ 0x + 0x0d + 0x0a +
+
+
+ + + Invite + 494e56495445207369703a30303937323339323837303434407369702e6379626572636974792e646b205349502f322e300d0a5669613a205349502f322e302f554450203139322e3136382e312e323b6272616e63683d7a39684734624b6e7032303233383237352d34373839323762613139322e3136382e312e323b72706f72740d0a46726f6d3a20226172696b22203c7369703a3335313034373233407369702e6379626572636974792e646b3e3b7461673d313735613164640d0a546f3a203c7369703a30303937323339323837303434407369702e6379626572636974792e646b3e0d0a43616c6c2d49443a2032343438373339312d3434396266326130403139322e3136382e312e320d0a435365713a203220494e564954450d0a50726f78792d417574686f72697a6174696f6e3a2044696765737420757365726e616d653d22766f693138303632222c7265616c6d3d227369702e6379626572636974792e646b222c7572693d227369703a3139322e3136382e312e32222c6e6f6e63653d2231373031623937623465623663363664363464306232306635613062656638222c6f70617175653d22313730316131333531663730373935222c6e633d223030303030303031222c726573706f6e73653d223062373939306262323164353537326436353731653937623938633664373066220d0a436f6e74656e742d547970653a206170706c69636174696f6e2f7364700d0a436f6e74656e742d4c656e6774683a203237300d0a446174653a204d6f6e2c203034204a756c20323030352030393a35343a323520474d540d0a436f6e746163743a203c7369703a3335313034373233403139322e3136382e312e323e0d0a457870697265733a203132300d0a4163636570743a206170706c69636174696f6e2f7364700d0a4d61782d466f7277617264733a2037300d0a557365722d4167656e743a204e65726f2053495050532049502050686f6e652056657273696f6e20322e302e35312e31360d0a416c6c6f773a20494e564954452c2041434b2c2043414e43454c2c204259452c2052454645522c204f5054494f4e532c204e4f544946592c20494e464f0d0a0d0a763d300d0a6f3d534950505320323434363634323220323434363634313820494e20495034203139322e3136382e312e320d0a733d5349502063616c6c0d0a633d494e20495034203139322e3136382e312e320d0a743d3020300d0a6d3d617564696f203330303030205254502f41565020302038203937203220330d0a613d7274706d61703a302070636d752f383030300d0a613d7274706d61703a382070636d612f383030300d0a613d7274706d61703a393720694c42432f383030300d0a613d7274706d61703a3220473732362d33322f383030300d0a613d7274706d61703a332047534d2f383030300d0a613d666d74703a3937206d6f64653d32300d0a613d73656e64726563760d0a + SipPDU + + 1076 + + + + + + INVITE + 0x20 + sip + 0x3a + 0097239287044@sip.cybercity.dk + 0x20 + SIP + 0x2f + 2.0 + 0x0d + 0x0a + + + +
+ Via + 0x3a + 0x20 + SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp20238275-478927ba192.168.1.2;rport + 0x0d + 0x0a +
+
+ From + 0x3a + 0x20 + "arik" <sip:35104723@sip.cybercity.dk>;tag=175a1dd + 0x0d + 0x0a +
+
+ To + 0x3a + 0x20 + <sip:0097239287044@sip.cybercity.dk> + 0x0d + 0x0a +
+
+ Call-ID + 0x3a + 0x20 + 24487391-449bf2a0@192.168.1.2 + 0x0d + 0x0a +
+
+ CSeq + 0x3a + 0x20 + 2 INVITE + 0x0d + 0x0a +
+
+ Proxy-Authorization + 0x3a + 0x20 + Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b97b4eb6c66d64d0b20f5a0bef8",opaque="1701a1351f70795",nc="00000001",response="0b7990bb21d5572d6571e97b98c6d70f" + 0x0d + 0x0a +
+
+ Content-Type + 0x3a + 0x20 + application/sdp + 0x0d + 0x0a +
+
+ Content-Length + 0x3a + 0x20 + 270 + 0x0d + 0x0a +
+
+ Date + 0x3a + 0x20 + Mon, 04 Jul 2005 09:54:25 GMT + 0x0d + 0x0a +
+
+ Contact + 0x3a + 0x20 + <sip:35104723@192.168.1.2> + 0x0d + 0x0a +
+
+ Expires + 0x3a + 0x20 + 120 + 0x0d + 0x0a +
+
+ Accept + 0x3a + 0x20 + application/sdp + 0x0d + 0x0a +
+
+ Max-Forwards + 0x3a + 0x20 + 70 + 0x0d + 0x0a +
+
+ User-Agent + 0x3a + 0x20 + Nero SIPPS IP Phone Version 2.0.51.16 + 0x0d + 0x0a +
+
+ Allow + 0x3a + 0x20 + INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO + 0x0d + 0x0a +
+
+ 0x0d0a763d300d0a6f3d534950505320323434363634323220323434363634313820494e20495034203139322e3136382e312e320d0a733d5349502063616c6c0d0a633d494e20495034203139322e3136382e312e320d0a743d3020300d0a6d3d617564696f203330303030205254502f41565020302038203937203220330d0a613d7274706d61703a302070636d752f383030300d0a613d7274706d61703a382070636d612f383030300d0a613d7274706d61703a393720694c42432f383030300d0a613d7274706d61703a3220473732362d33322f383030300d0a613d7274706d61703a332047534d2f383030300d0a613d666d74703a3937206d6f64653d32300d0a613d73656e6472656376 + 0x0d + 0x0a +
+
+
+ +
\ No newline at end of file