Wireshark dissector for Pro DJ Link protocol
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

TCP reassembly works for db messages

Stella 8d7c8c92 efdad242

+112 -42
+112 -42
pro_dj_link.lua
··· 616 616 -- DB SERVER DISSECTION LOGIC 617 617 -- ============================================================================ 618 618 619 + -- Helper to calculate the full byte length of a DB message starting at `offset`. 620 + -- Returns: 621 + -- > 0: The total byte length of the complete message. 622 + -- < 0: The number of missing bytes needed to complete the message (returns -missing). 623 + -- 0: Not a valid DB message or parse error. 624 + local function get_db_msg_len(buf, offset) 625 + local len = buf:len() 626 + local available = len - offset 627 + 628 + if available < 5 then return -(5 - available) end 629 + 630 + local tag = buf(offset, 1):uint() 631 + local magic = buf(offset+1, 4):uint() 632 + 633 + if tag == 0x11 and magic == 0x00000001 then 634 + return 5 635 + end 636 + 637 + if tag ~= 0x11 or magic ~= 0x872349ae then 638 + return 0 639 + end 640 + 641 + local cur = offset + 5 642 + 643 + local function read_field() 644 + local a = len - cur 645 + if a < 1 then return false, (1 - a) end 646 + local ftag = buf(cur, 1):uint() 647 + if ftag == 0x0f then 648 + if a < 2 then return false, (2 - a) end 649 + local val = buf(cur+1, 1):uint() 650 + cur = cur + 2 651 + return true, val 652 + elseif ftag == 0x10 then 653 + if a < 3 then return false, (3 - a) end 654 + local val = buf(cur+1, 2):uint() 655 + cur = cur + 3 656 + return true, val 657 + elseif ftag == 0x11 then 658 + if a < 5 then return false, (5 - a) end 659 + local val = buf(cur+1, 4):uint() 660 + cur = cur + 5 661 + return true, val 662 + elseif ftag == 0x14 then 663 + if a < 5 then return false, (5 - a) end 664 + local blen = buf(cur+1, 4):uint() 665 + if a < 5 + blen then return false, (5 + blen - a) end 666 + cur = cur + 5 + blen 667 + return true, nil 668 + elseif ftag == 0x26 then 669 + if a < 5 then return false, (5 - a) end 670 + local chars = buf(cur+1, 4):uint() 671 + local blen = chars * 2 672 + if a < 5 + blen then return false, (5 + blen - a) end 673 + cur = cur + 5 + blen 674 + return true, nil 675 + else 676 + return false, nil 677 + end 678 + end 679 + 680 + local ok, val = read_field() -- Tx ID 681 + if not ok then return val and -val or 0 end 682 + 683 + ok, val = read_field() -- Msg Type 684 + if not ok then return val and -val or 0 end 685 + 686 + ok, val = read_field() -- Arg Count 687 + if not ok then return val and -val or 0 end 688 + local arg_count = val 689 + 690 + local a = len - cur 691 + if a < 1 then return -(1 - a) end 692 + if buf(cur, 1):uint() == 0x14 then 693 + ok, val = read_field() 694 + if not ok then return val and -val or 0 end 695 + end 696 + 697 + for i = 1, arg_count do 698 + ok, val = read_field() 699 + if not ok then return val and -val or 0 end 700 + end 701 + 702 + return cur - offset 703 + end 704 + 619 705 local function dissect_db_tcp(buf, pkt, root, length) 620 706 local off = 0 621 707 local found_db = false ··· 624 710 625 711 local src_port = pkt.src_port 626 712 local dst_port = pkt.dst_port 627 - local is_static_pdjl_port = (src_port == 12523 or dst_port == 12523 or src_port == 1051 or dst_port == 1051 or src_port == 1052 or dst_port == 1052) 628 713 local stream_id = get_stream_key(pkt) 629 714 630 - -- Check for continuation at the start of the TCP segment 631 - -- If this stream has been marked as PDJL before, or is on a static port, we check for continuation 632 - if (is_static_pdjl_port or pdjl_streams[stream_id]) and length > 0 then 633 - local tag = buf(0, 1):uint() 634 - local val = length >= 5 and buf(1, 4):uint() or 0 635 - if not (tag == 0x11 and (val == 0x872349ae or val == 0x00000001)) then 636 - -- Not a new message start, find the next magic header if it exists in this same packet 637 - local next_msg = -1 638 - for i = 1, length - 5 do 639 - if buf(i, 1):uint() == 0x11 and buf(i+1, 4):uint() == 0x872349ae then 640 - next_msg = i 641 - break 642 - end 643 - end 644 - 645 - local cont_len = (next_msg == -1) and length or next_msg 646 - local pdjl_tree = root:add(p_djl, buf(0, cont_len)) 647 - pdjl_tree:set_text("Pro DJ Link (Blob Chunk)") 648 - table.insert(db_types, "Blob Continuation") 649 - found_db = true 650 - off = cont_len 651 - pkt.cols.protocol = "Pro DJ Link (DB)" 652 - 653 - -- Mark this stream as PDJL even if we haven't seen the magic yet 654 - pdjl_streams[stream_id] = true 715 + while off < length do 716 + local available = length - off 717 + if available < 5 then 718 + pkt.desegment_offset = off 719 + pkt.desegment_len = 5 - available 720 + return true 655 721 end 656 - end 657 722 658 - while off + 5 <= length do 659 723 local tag = buf(off, 1):uint() 660 724 local magic_or_val = buf(off+1, 4):uint() 661 725 662 726 if tag == 0x11 and (magic_or_val == 0x872349ae or magic_or_val == 0x00000001) then 663 - -- Mark this stream as PDJL since it contains the magic or greeting 664 727 pdjl_streams[stream_id] = true 665 728 729 + local msg_len = get_db_msg_len(buf, off) 730 + if msg_len < 0 then 731 + pkt.desegment_offset = off 732 + pkt.desegment_len = -msg_len 733 + return true 734 + elseif msg_len == 0 then 735 + break -- parse error, stop processing 736 + end 737 + 666 738 if magic_or_val == 0x872349ae then 667 739 if not found_db then 668 740 pkt.cols.protocol = "Pro DJ Link (DB)" 669 741 found_db = true 670 742 end 671 743 672 - local msg_tree = root:add(p_djl, buf(off, 0)) -- Set length later 744 + local msg_tree = root:add(p_djl, buf(off, msg_len)) 673 745 msg_tree:add(f_db_magic, buf(off+1, 4)) 674 746 675 747 local current = off + 5 676 748 677 749 -- Transaction ID 678 750 local tx_id = 0 679 - if length - current >= 5 then 751 + if msg_len - (current - off) >= 5 then 680 752 local bytes_read, val = dissect_db_field(buf, current, msg_tree, nil, "Transaction ID") 681 753 tx_id = val or 0 682 754 local tx_str = (tx_id == 0xfffffffe) and "Setup/Teardown" or string.format("0x%08x", tx_id) 683 755 684 - -- Only add to the summary list if it's a new unique ID for this packet 685 756 local already_added = false 686 757 for _, v in ipairs(tx_ids) do 687 758 if v == tx_str then already_added = true break end ··· 695 766 696 767 -- Message Type 697 768 local m_type = 0 698 - if length - current >= 3 then 769 + if msg_len - (current - off) >= 3 then 699 770 local bytes_read, val, m_type_item = dissect_db_field(buf, current, msg_tree, nil, "Message Type") 700 771 m_type = val or 0 701 772 local m_type_str = DB_MSG_TYPES[m_type] or string.format("Unknown(0x%04x)", m_type) 702 773 703 - -- Specific label for Device Disconnect when tx_id is Setup/Teardown 704 774 if m_type == 0x0100 and tx_id == 0xfffffffe then 705 775 m_type_str = "Device Disconnect" 706 776 if m_type_item then ··· 716 786 717 787 -- Arg Count 718 788 local arg_count = 0 719 - if length - current >= 2 then 789 + if msg_len - (current - off) >= 2 then 720 790 local bytes_read, val = dissect_db_field(buf, current, msg_tree, nil, "Arg Count") 721 791 arg_count = val or 0 722 792 current = bytes_read 723 793 end 724 794 725 795 -- Arg Tag Blob (usually 12 bytes) 726 - if length - current >= 5 and buf(current, 1):uint() == 0x14 then 796 + if msg_len - (current - off) >= 5 and buf(current, 1):uint() == 0x14 then 727 797 local tag_len = buf(current+1, 4):uint() 728 - if length - current >= 5 + tag_len then 798 + if msg_len - (current - off) >= 5 + tag_len then 729 799 msg_tree:add(f_db_tag_blob, buf(current+5, tag_len)) 730 800 current = current + 5 + tag_len 731 801 end ··· 733 803 734 804 -- Arguments 735 805 for i = 1, arg_count do 736 - if current >= length then break end 806 + if current >= off + msg_len then break end 737 807 local prev_current = current 738 808 local bytes_read, val = dissect_db_field(buf, current, msg_tree, m_type, i) 739 809 ··· 766 836 end 767 837 768 838 -- Special handling for trailing binary data in blob responses 769 - if (m_type == 0x4002 or m_type == 0x4602 or m_type == 0x4702 or m_type == 0x4e02 or m_type == 0x4a02 or m_type == 0x4402 or m_type == 0x4f02) and current < length then 839 + if (m_type == 0x4002 or m_type == 0x4602 or m_type == 0x4702 or m_type == 0x4e02 or m_type == 0x4a02 or m_type == 0x4402 or m_type == 0x4f02) and current < off + msg_len then 770 840 local label = get_arg_label(m_type, 4) or "Blob Data" 771 - local remaining = length - current 841 + local remaining = (off + msg_len) - current 772 842 msg_tree:add(f_db_field_bin, buf(current, remaining)):set_text(label .. ": [" .. remaining .. " bytes]") 773 843 current = current + remaining 774 844 end 775 845 776 - msg_tree:set_len(current - off) 777 - off = current 846 + msg_tree:set_len(msg_len) 847 + off = off + msg_len 778 848 elseif magic_or_val == 0x00000001 then 779 849 if not found_db then 780 850 pkt.cols.protocol = "Pro DJ Link (DB)"