package io.sipstack.netty.codec.sip; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.DatagramPacket; import io.netty.handler.codec.MessageToMessageDecoder; import io.pkts.buffer.Buffer; import io.pkts.buffer.Buffers; import io.pkts.packet.sip.SipMessage; import io.pkts.packet.sip.impl.SipParser; import java.util.List; /** * The {@link SipMessageDatagramDecoder} will frame an incoming UDP packet into * a {@link SipMessage}. Since the data will only be framed, only very minimal * checking of whether the data is actually a valid SIP message or not will be * performed. It is up to the user to validate the SipMessage through the method * {@link SipMessage#verify()}. The philosophy is to simply just frame things as * fast as possible and then do lazy parsing as much as possible. * * @author [email protected] */ public final class SipMessageDatagramDecoder extends MessageToMessageDecoder<DatagramPacket> { private final Clock clock; public SipMessageDatagramDecoder() { this.clock = new SystemClock(); } public SipMessageDatagramDecoder(final Clock clock) { this.clock = clock; } /** * Framing an UDP packet is much simpler than for a stream based protocol * like TCP. We just assumes that everything is correct and therefore all is * needed is to read the first line, which is assumed to be a SIP initial * line, then read all headers as one big block and whatever is left better * be the payload (if there is one). * * Of course, things do go wrong. If e.g. the UDP packet is fragmented, then * we may end up with a partial SIP message but the user can either decide * to double check things by calling {@link SipMessage#verify()} or the user * will eventually notice when trying to access partial headers etc. * */ @Override protected void decode(final ChannelHandlerContext ctx, final DatagramPacket msg, final List<Object> out) throws Exception { final long arrivalTime = this.clock.getCurrentTimeMillis(); final ByteBuf content = msg.content(); // some clients are sending various types of pings even over // UDP, such as linphone which is sending "jaK\n\r". // According to RFC5626, the only valid ping over UDP // is to use a STUN request and since such a request is // at least 20 bytes we will simply ignore anything less // than that. And yes, there is no way that an actual // SIP message ever could be less than 20 bytes. if (content.readableBytes() < 20) { return; } final byte[] b = new byte[content.readableBytes()]; content.getBytes(0, b); final Buffer buffer = Buffers.wrap(b); SipParser.consumeSWS(buffer); final SipMessage sipMessage = SipParser.frame(buffer); // System.err.println("CSeq header: " + sipMessage.getCSeqHeader()); // final SipInitialLine initialLine = SipInitialLine.parse(buffer.readLine()); // final Buffer headers = buffer.readUntilDoubleCRLF(); // SipMessage sipMessage = null; // if (initialLine.isRequestLine()) { // sipMessage = new SipRequestImpl(initialLine.toRequestLine(), headers, buffer); // } else { // sipMessage = new SipResponseImpl(initialLine.toResponseLine(), headers, buffer); // } final Connection connection = new UdpConnection(ctx.channel(), msg.sender()); final SipMessageEvent event = new DefaultSipMessageEvent(connection, sipMessage, arrivalTime); out.add(event); } }