优化海康发送的Message消息设置的消息体长度为0,导致无法解析的问题 #920

This commit is contained in:
648540858 2023-07-07 14:51:41 +08:00
parent ee1a823cb6
commit 70933aef8c
6 changed files with 487 additions and 9 deletions

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.GbStringMsgParserFactory;
import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties;
import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
import gov.nist.javax.sip.SipProviderImpl;
@ -63,6 +64,7 @@ public class SipLayer implements CommandLineRunner {
SipStackImpl sipStack;
try {
sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()));
sipStack.setMessageParserFactory(new GbStringMsgParserFactory());
} catch (PeerUnavailableException e) {
logger.error("[SIP SERVER] SIP服务启动失败 监听地址{}失败,请检查ip是否正确", monitorIp);
return;
@ -75,7 +77,6 @@ public class SipLayer implements CommandLineRunner {
tcpSipProvider.setDialogErrorsAutomaticallyHandled();
tcpSipProvider.addSipListener(sipProcessorObserver);
tcpSipProviderMap.put(monitorIp, tcpSipProvider);
logger.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException
| TooManyListenersException

View File

@ -0,0 +1,457 @@
package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.core.CommonLogger;
import gov.nist.core.Host;
import gov.nist.core.HostNameParser;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.SIPConstants;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.GenericURI;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.address.TelephoneNumber;
import gov.nist.javax.sip.header.*;
import gov.nist.javax.sip.message.SIPMessage;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.parser.*;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
public class GBStringMsgParser implements MessageParser {
protected static boolean computeContentLengthFromMessage = false;
private static StackLogger logger = CommonLogger.getLogger(StringMsgParser.class);
/**
* @since v0.9
*/
public GBStringMsgParser() {
super();
}
/**
* Parse a buffer containing a single SIP Message where the body is an array
* of un-interpreted bytes. This is intended for parsing the message from a
* memory buffer when the buffer. Incorporates a bug fix for a bug that was
* noted by Will Sullin of Callcast
*
* @param msgBuffer
* a byte buffer containing the messages to be parsed. This can
* consist of multiple SIP Messages concatenated together.
* @return a SIPMessage[] structure (request or response) containing the
* parsed SIP message.
* @exception ParseException
* is thrown when an illegal message has been encountered
* (and the rest of the buffer is discarded).
* @see ParseExceptionListener
*/
public SIPMessage parseSIPMessage(byte[] msgBuffer, boolean readBody, boolean strict, ParseExceptionListener parseExceptionListener) throws ParseException {
if (msgBuffer == null || msgBuffer.length == 0)
return null;
int i = 0;
// Squeeze out any leading control character.
try {
while (msgBuffer[i] < 0x20)
i++;
}
catch (ArrayIndexOutOfBoundsException e) {
// Array contains only control char, return null.
if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
logger.logDebug("handled only control char so returning null");
}
return null;
}
// Iterate thru the request/status line and headers.
String currentLine = null;
String currentHeader = null;
boolean isFirstLine = true;
SIPMessage message = null;
do
{
int lineStart = i;
// Find the length of the line.
try {
while (msgBuffer[i] != '\r' && msgBuffer[i] != '\n')
i++;
}
catch (ArrayIndexOutOfBoundsException e) {
// End of the message.
break;
}
int lineLength = i - lineStart;
// Make it a String.
try {
currentLine = new String(msgBuffer, lineStart, lineLength, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new ParseException("Bad message encoding!", 0);
}
currentLine = trimEndOfLine(currentLine);
if (currentLine.length() == 0) {
// Last header line, process the previous buffered header.
if (currentHeader != null && message != null) {
processHeader(currentHeader, message, parseExceptionListener, msgBuffer);
}
}
else {
if (isFirstLine) {
message = processFirstLine(currentLine, parseExceptionListener, msgBuffer);
} else {
char firstChar = currentLine.charAt(0);
if (firstChar == '\t' || firstChar == ' ') {
if (currentHeader == null)
throw new ParseException("Bad header continuation.", 0);
// This is a continuation, append it to the previous line.
currentHeader += currentLine.substring(1);
}
else {
if (currentHeader != null && message != null) {
processHeader(currentHeader, message, parseExceptionListener, msgBuffer);
}
currentHeader = currentLine;
}
}
}
if (msgBuffer[i] == '\r' && msgBuffer.length > i+1 && msgBuffer[i+1] == '\n')
i++;
i++;
isFirstLine = false;
} while (currentLine.length() > 0); // End do - while
if (message == null) throw new ParseException("Bad message", 0);
message.setSize(i);
// Check for content legth header
if (readBody && message.getContentLength() != null ) {
if ( message.getContentLength().getContentLength() != 0) {
int bodyLength = msgBuffer.length - i;
byte[] body = new byte[bodyLength];
System.arraycopy(msgBuffer, i, body, 0, bodyLength);
message.setMessageContent(body,!strict,computeContentLengthFromMessage,message.getContentLength().getContentLength());
} else if (message.getCSeqHeader().getMethod().equalsIgnoreCase("MESSAGE")) {
int bodyLength = msgBuffer.length - i;
byte[] body = new byte[bodyLength];
System.arraycopy(msgBuffer, i, body, 0, bodyLength);
message.setMessageContent(body,!strict,computeContentLengthFromMessage,bodyLength);
}else if (!computeContentLengthFromMessage && strict) {
String last4Chars = new String(msgBuffer, msgBuffer.length - 4, 4);
if(!"\r\n\r\n".equals(last4Chars)) {
throw new ParseException("Extraneous characters at the end of the message ",i);
}
}
}
return message;
}
protected static String trimEndOfLine(String line) {
if (line == null)
return line;
int i = line.length() - 1;
while (i >= 0 && line.charAt(i) <= 0x20)
i--;
if (i == line.length() - 1)
return line;
if (i == -1)
return "";
return line.substring(0, i+1);
}
protected SIPMessage processFirstLine(String firstLine, ParseExceptionListener parseExceptionListener, byte[] msgBuffer) throws ParseException {
SIPMessage message;
if (!firstLine.startsWith(SIPConstants.SIP_VERSION_STRING)) {
message = new SIPRequest();
try {
RequestLine requestLine = new RequestLineParser(firstLine + "\n")
.parse();
((SIPRequest) message).setRequestLine(requestLine);
} catch (ParseException ex) {
if (parseExceptionListener != null)
try {
parseExceptionListener.handleException(ex, message,
RequestLine.class, firstLine, new String(msgBuffer, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
else
throw ex;
}
} else {
message = new SIPResponse();
try {
StatusLine sl = new StatusLineParser(firstLine + "\n").parse();
((SIPResponse) message).setStatusLine(sl);
} catch (ParseException ex) {
if (parseExceptionListener != null) {
try {
parseExceptionListener.handleException(ex, message,
StatusLine.class, firstLine, new String(msgBuffer, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else
throw ex;
}
}
return message;
}
protected void processHeader(String header, SIPMessage message, ParseExceptionListener parseExceptionListener, byte[] rawMessage) throws ParseException {
if (header == null || header.length() == 0)
return;
HeaderParser headerParser = null;
try {
headerParser = ParserFactory.createParser(header + "\n");
} catch (ParseException ex) {
// https://java.net/jira/browse/JSIP-456
if (parseExceptionListener != null) {
parseExceptionListener.handleException(ex, message, null,
header, null);
return;
} else {
throw ex;
}
}
try {
SIPHeader sipHeader = headerParser.parse();
message.attachHeader(sipHeader, false);
} catch (ParseException ex) {
if (parseExceptionListener != null) {
String headerName = Lexer.getHeaderName(header);
Class headerClass = NameMap.getClassFromName(headerName);
if (headerClass == null) {
headerClass = ExtensionHeaderImpl.class;
}
try {
parseExceptionListener.handleException(ex, message,
headerClass, header, new String(rawMessage, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
/**
* Parse an address (nameaddr or address spec) and return and address
* structure.
*
* @param address
* is a String containing the address to be parsed.
* @return a parsed address structure.
* @since v1.0
* @exception ParseException
* when the address is badly formatted.
*/
public AddressImpl parseAddress(String address) throws ParseException {
AddressParser addressParser = new AddressParser(address);
return addressParser.address(true);
}
/**
* Parse a host:port and return a parsed structure.
*
* @param hostport
* is a String containing the host:port to be parsed
* @return a parsed address structure.
* @since v1.0
* @exception throws
* a ParseException when the address is badly formatted.
*
public HostPort parseHostPort(String hostport) throws ParseException {
Lexer lexer = new Lexer("charLexer", hostport);
return new HostNameParser(lexer).hostPort();
}
*/
/**
* Parse a host name and return a parsed structure.
*
* @param host
* is a String containing the host name to be parsed
* @return a parsed address structure.
* @since v1.0
* @exception ParseException
* a ParseException when the hostname is badly formatted.
*/
public Host parseHost(String host) throws ParseException {
Lexer lexer = new Lexer("charLexer", host);
return new HostNameParser(lexer).host();
}
/**
* Parse a telephone number return a parsed structure.
*
* @param telephone_number
* is a String containing the telephone # to be parsed
* @return a parsed address structure.
* @since v1.0
* @exception ParseException
* a ParseException when the address is badly formatted.
*/
public TelephoneNumber parseTelephoneNumber(String telephone_number)
throws ParseException {
// Bug fix contributed by Will Scullin
return new URLParser(telephone_number).parseTelephoneNumber(true);
}
/**
* Parse a SIP url from a string and return a URI structure for it.
*
* @param url
* a String containing the URI structure to be parsed.
* @return A parsed URI structure
* @exception ParseException
* if there was an error parsing the message.
*/
public SipUri parseSIPUrl(String url) throws ParseException {
try {
return new URLParser(url).sipURL(true);
} catch (ClassCastException ex) {
throw new ParseException(url + " Not a SIP URL ", 0);
}
}
/**
* Parse a uri from a string and return a URI structure for it.
*
* @param url
* a String containing the URI structure to be parsed.
* @return A parsed URI structure
* @exception ParseException
* if there was an error parsing the message.
*/
public GenericURI parseUrl(String url) throws ParseException {
return new URLParser(url).parse();
}
/**
* Parse an individual SIP message header from a string.
*
* @param header
* String containing the SIP header.
* @return a SIPHeader structure.
* @exception ParseException
* if there was an error parsing the message.
*/
public static SIPHeader parseSIPHeader(String header) throws ParseException {
int start = 0;
int end = header.length() - 1;
try {
// Squeeze out any leading control character.
while (header.charAt(start) <= 0x20)
start++;
// Squeeze out any trailing control character.
while (header.charAt(end) <= 0x20)
end--;
}
catch (ArrayIndexOutOfBoundsException e) {
// Array contains only control char.
throw new ParseException("Empty header.", 0);
}
StringBuilder buffer = new StringBuilder(end + 1);
int i = start;
int lineStart = start;
boolean endOfLine = false;
while (i <= end) {
char c = header.charAt(i);
if (c == '\r' || c == '\n') {
if (!endOfLine) {
buffer.append(header.substring(lineStart, i));
endOfLine = true;
}
}
else {
if (endOfLine) {
endOfLine = false;
if (c == ' ' || c == '\t') {
buffer.append(' ');
lineStart = i + 1;
}
else {
lineStart = i;
}
}
}
i++;
}
buffer.append(header.substring(lineStart, i));
buffer.append('\n');
HeaderParser hp = ParserFactory.createParser(buffer.toString());
if (hp == null)
throw new ParseException("could not create parser", 0);
return hp.parse();
}
/**
* Parse the SIP Request Line
*
* @param requestLine
* a String containing the request line to be parsed.
* @return a RequestLine structure that has the parsed RequestLine
* @exception ParseException
* if there was an error parsing the requestLine.
*/
public RequestLine parseSIPRequestLine(String requestLine)
throws ParseException {
requestLine += "\n";
return new RequestLineParser(requestLine).parse();
}
/**
* Parse the SIP Response message status line
*
* @param statusLine
* a String containing the Status line to be parsed.
* @return StatusLine class corresponding to message
* @exception ParseException
* if there was an error parsing
* @see StatusLine
*/
public StatusLine parseSIPStatusLine(String statusLine)
throws ParseException {
statusLine += "\n";
return new StatusLineParser(statusLine).parse();
}
public static void setComputeContentLengthFromMessage(
boolean computeContentLengthFromMessage) {
GBStringMsgParser.computeContentLengthFromMessage = computeContentLengthFromMessage;
}
}

View File

@ -8,7 +8,7 @@ import java.util.*;
/**
* 重写jain sip的SIPDate解决与国标时间格式不一致的问题
*/
public class WvpSipDate extends SIPDate {
public class GbSipDate extends SIPDate {
/**
*
@ -17,7 +17,7 @@ public class WvpSipDate extends SIPDate {
private Calendar javaCal;
public WvpSipDate(long timeMillis) {
public GbSipDate(long timeMillis) {
this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
Date date = new Date(timeMillis);
this.javaCal.setTime(date);

View File

@ -0,0 +1,21 @@
package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.javax.sip.parser.MessageParser;
import gov.nist.javax.sip.parser.MessageParserFactory;
import gov.nist.javax.sip.stack.SIPTransactionStack;
public class GbStringMsgParserFactory implements MessageParserFactory {
/**
* msg parser is completely stateless, reuse isntance for the whole stack
* fixes https://github.com/RestComm/jain-sip/issues/92
*/
private static GBStringMsgParser msgParser = new GBStringMsgParser();
/*
* (non-Javadoc)
* @see gov.nist.javax.sip.parser.MessageParserFactory#createMessageParser(gov.nist.javax.sip.stack.SIPTransactionStack)
*/
public MessageParser createMessageParser(SIPTransactionStack stack) {
return msgParser;
}
}

View File

@ -6,7 +6,7 @@ import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
import com.genersoft.iot.vmp.gb28181.bean.GbSipDate;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
@ -145,8 +145,8 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
// 添加date头
SIPDateHeader dateHeader = new SIPDateHeader();
// 使用自己修改的
WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
dateHeader.setDate(wvpSipDate);
GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
dateHeader.setDate(gbSipDate);
response.addHeader(dateHeader);
if (request.getExpires() == null) {
@ -218,8 +218,8 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
// 添加date头
SIPDateHeader dateHeader = new SIPDateHeader();
// 使用自己修改的
WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
dateHeader.setDate(wvpSipDate);
GbSipDate gbSipDate = new GbSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
dateHeader.setDate(gbSipDate);
response.addHeader(dateHeader);
// 添加Contact头

View File

@ -470,7 +470,6 @@ public class DeviceQuery {
public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) {
try {
final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());
resp.setContentType(MediaType.IMAGE_PNG_VALUE);
IOUtils.copy(in, resp.getOutputStream());