Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci-pytest-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
fail-fast: true # all versions must be supported

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-pytest-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
fail-fast: true # all versions must be supported

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-pytest-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
fail-fast: true # all versions must be supported

steps:
Expand Down
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,32 @@ include(cmake/icspacket-defs.cmake)
# -----------------------------------------------------------------------------
# icspacket ASN.1 extensions
# -----------------------------------------------------------------------------
# All extensions are generated with RFILL (random value generation for
# testing), CBOR and *PER (OER/UPER/APER) codec support disabled - icspacket
# only needs BER/XER/JER on the wire.
set(ICS_ASN1_DISABLED_CODEC_DEFINITIONS
ASN_DISABLE_OER_SUPPORT
ASN_DISABLE_UPER_SUPPORT
ASN_DISABLE_APER_SUPPORT
ASN_DISABLE_CBOR_SUPPORT
ASN_DISABLE_RFILL_SUPPORT
)

add_asn1_extension(
NAME _iso8823
DIR "${ICS_SOURCE_DIR}/proto/iso_pres/_iso8823"
INSTALL "proto/iso_pres"
DEFINITIONS ${ICS_ASN1_DISABLED_CODEC_DEFINITIONS}
)
add_asn1_extension(
NAME _mms
DIR "${ICS_SOURCE_DIR}/proto/mms/_mms"
INSTALL "proto/mms"
DEFINITIONS ${ICS_ASN1_DISABLED_CODEC_DEFINITIONS}
)
add_asn1_extension(
NAME _iec61850
DIR "${ICS_SOURCE_DIR}/proto/iec61850/_iec61850"
INSTALL "proto/iec61850"
DEFINITIONS ${ICS_ASN1_DISABLED_CODEC_DEFINITIONS}
)
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ systems using their protocols. Currently supported protocols are:
- DNP3.0 (IEEE 1815)
- MMS (ISO 9506) and mappings for IEC 61850
- IEC 60870-5-104
- MMS (ISO 9506) and mappings for IEC 61850
- ACSE (X.227)
- COPP (X.226)
- COSP (X.225)
Expand Down
8 changes: 8 additions & 0 deletions cmake/icspacket-common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,13 @@ function(add_asn1_extension)
TARGETS ${ASN1_NAME}
DESTINATION ${SKBUILD_PROJECT_NAME}/${ASN1_INSTALL}
)

# Install the generated Python type stub (.pyi), if present.
if(EXISTS "${ASN1_DIR}.pyi")
install(
FILES "${ASN1_DIR}.pyi"
DESTINATION ${SKBUILD_PROJECT_NAME}/${ASN1_INSTALL}
)
endif()
endfunction()

67 changes: 67 additions & 0 deletions scripts/regen-asn1.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
#
# Regenerates the C sources/headers, skeletons and Python (.pyi) stubs for
# all icspacket ASN.1 extensions (_iso8823, _mms, _iec61850) using the
# asn1c-bindings compiler (https://github.com/MatrixEditor/asn1c-bindings).
#
# Usage:
# A1C_PATH=/path/to/asn1c-bindings/asn1c/asn1c \
# A1C_SKELETONS_PATH=/path/to/asn1c-bindings/skeletons \
# ./scripts/regen-asn1.sh
#
# Env vars:
# A1C_PATH Path to the asn1c-bindings compiler binary.
# Defaults to "asn1c" (looked up on PATH).
# A1C_SKELETONS_PATH Path to the asn1c-bindings skeletons/ directory.
# Required (no sane default exists).
set -euo pipefail

: "${A1C_PATH:=asn1c}"
if [ -z "${A1C_SKELETONS_PATH:-}" ]; then
echo "error: A1C_SKELETONS_PATH must be set to the asn1c-bindings skeletons/ directory" >&2
exit 1
fi

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PROTO_DIR="$ROOT_DIR/src/icspacket/proto"
SKELETON_OUT_DIR="$ROOT_DIR/src/icspacket/include/skeletons"

COMMON_FLAGS=(
-no-gen-example
-no-gen-autotools
-fcompound-names
-Wdebug-compiler
-S "$A1C_SKELETONS_PATH"
-out-skeletons="$SKELETON_OUT_DIR"
-out-skip-imports
-no-gen-OER
-no-gen-UPER
-no-gen-APER
-no-gen-CBOR
-no-gen-random-fill
)

# generate <module-name> <extension-src-dir> <asn1-file>...
generate() {
local name="$1" dir="$2"
shift 2

echo "==> Generating ${name} (sources: $*)"
"$A1C_PATH" "${COMMON_FLAGS[@]}" \
-M "$name" \
-out-python-stubs="$(dirname "$dir")" \
-out-python-sources="$dir" \
"$@"
}

generate _iso8823 "$PROTO_DIR/iso_pres/_iso8823" \
"$PROTO_DIR/iso_pres/_iso8823.asn1"

generate _mms "$PROTO_DIR/mms/_mms" \
"$PROTO_DIR/_acse.asn1" "$PROTO_DIR/mms/_mms.asn1"

generate _iec61850 "$PROTO_DIR/iec61850/_iec61850" \
"$PROTO_DIR/iec61850/_iec61850.asn1"

echo
echo "Done."
14 changes: 9 additions & 5 deletions src/icspacket/core/hexdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from string import ascii_letters
from io import StringIO
from string import ascii_letters

#: Precomputed set of valid ASCII letter byte values.
#:
Expand Down Expand Up @@ -54,7 +54,8 @@ def hexdump(data: bytes, width: int = 16) -> str:

:return:
A string containing the formatted hexdump, with newline
terminators at the end of each row.
terminators at the end of each row. Empty input returns an
empty string.
:rtype: str

:raises ValueError:
Expand All @@ -64,8 +65,8 @@ def hexdump(data: bytes, width: int = 16) -> str:

>>> data = b"Hello, MMS!"
>>> print(hexdump(data, width=8))
00000000: 48 65 6c 6c 6f 2c 20 4d Hello,.M
00000008: 4d 53 21 MS!
00000000: 48 65 6c 6c 6f 2c 20 4d Hello..M
00000008: 4d 53 21 MS.

.. note::
Unlike the standard UNIX ``hexdump`` tool, this implementation
Expand All @@ -76,10 +77,13 @@ def hexdump(data: bytes, width: int = 16) -> str:
if width < 1:
raise ValueError("hexdump width must be >= 1")

if not data:
return ""

windows_size = min(len(data), width)
result = StringIO()
for index in range(0, len(data), windows_size):
offset = index * windows_size
offset = index
chunk = data[index : index + windows_size]

chunk_hex = chunk.hex(sep=" ")
Expand Down
18 changes: 13 additions & 5 deletions src/icspacket/include/skeletons/ANY.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
asn_OCTET_STRING_specifics_t asn_SPC_ANY_specs = {
sizeof(ANY_t), offsetof(ANY_t, _asn_ctx), ASN_OSUBV_ANY};
asn_TYPE_operation_t asn_OP_ANY = {
.kind = ASN_KIND_PRIMITIVE,
OCTET_STRING_free,
#if !defined(ASN_DISABLE_PRINT_SUPPORT)
OCTET_STRING_print,
Expand Down Expand Up @@ -38,8 +39,8 @@ asn_TYPE_operation_t asn_OP_ANY = {
0,
#endif /* !defined(ASN_DISABLE_JER_SUPPORT) */
#if !defined(ASN_DISABLE_OER_SUPPORT)
0,
0,
OCTET_STRING_decode_oer,
OCTET_STRING_encode_oer,
#else
0,
0,
Expand All @@ -57,9 +58,16 @@ asn_TYPE_operation_t asn_OP_ANY = {
#else
0,
0,
#endif /* !defined(ASN_DISABLE_APER_SUPPORT) */
0, /* Random fill is not defined for ANY type */
0 /* Use generic outmost tag fetcher */
#endif /* !defined(ASN_DISABLE_APER_SUPPORT) */
0, /* Random fill is not defined for ANY type */
0, /* Use generic outmost tag fetcher */
#if !defined(ASN_DISABLE_CBOR_SUPPORT)
OCTET_STRING_decode_cbor, /* Reuse OCTET STRING decoder (raw DER bytes) */
OCTET_STRING_encode_cbor, /* Reuse OCTET STRING encoder (raw DER bytes) */
#else
0,
0,
#endif /* !defined(ASN_DISABLE_CBOR_SUPPORT) */
};
asn_TYPE_descriptor_t asn_DEF_ANY = {
"ANY",
Expand Down
5 changes: 5 additions & 0 deletions src/icspacket/include/skeletons/ANY.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ jer_type_decoder_f ANY_decode_jer;
jer_type_encoder_f ANY_encode_jer;
#endif /* !defined(ASN_DISABLE_JER_SUPPORT) */

#if !defined(ASN_DISABLE_OER_SUPPORT)
#define ANY_decode_oer OCTET_STRING_decode_oer
#define ANY_encode_oer OCTET_STRING_encode_oer
#endif /* !defined(ASN_DISABLE_OER_SUPPORT) */

#if !defined(ASN_DISABLE_UPER_SUPPORT)
per_type_decoder_f ANY_decode_uper;
per_type_encoder_f ANY_encode_uper;
Expand Down
17 changes: 7 additions & 10 deletions src/icspacket/include/skeletons/ANY_jer.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
#include <asn_internal.h>
#include <ANY.h>

asn_enc_rval_t ANY_encode_jer(const asn_TYPE_descriptor_t *td,
const asn_jer_constraints_t *constraints,
const void *sptr, int ilevel,
enum jer_encoder_flags_e flags,
asn_app_consume_bytes_f *cb, void *app_key) {
ASN__ENCODE_FAILED;

/* Dump as binary */
return OCTET_STRING_encode_jer(td, constraints, sptr, ilevel, flags, cb,
app_key);
asn_enc_rval_t
ANY_encode_jer(const asn_TYPE_descriptor_t *td, const asn_jer_constraints_t *constraints,
const void *sptr, int ilevel,
enum jer_encoder_flags_e flags, asn_app_consume_bytes_f *cb,
void *app_key) {
/* Dump as binary */
return OCTET_STRING_encode_jer(td, constraints, sptr, ilevel, flags, cb, app_key);
}
16 changes: 12 additions & 4 deletions src/icspacket/include/skeletons/BIT_STRING.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ static const ber_tlv_tag_t asn_DEF_BIT_STRING_tags[] = {
asn_OCTET_STRING_specifics_t asn_SPC_BIT_STRING_specs = {
sizeof(BIT_STRING_t), offsetof(BIT_STRING_t, _asn_ctx), ASN_OSUBV_BIT};
asn_TYPE_operation_t asn_OP_BIT_STRING = {
OCTET_STRING_free, /* Implemented in terms of OCTET STRING */
.kind = ASN_KIND_PRIMITIVE,
OCTET_STRING_free, /* Implemented in terms of OCTET STRING */
#if !defined(ASN_DISABLE_PRINT_SUPPORT)
BIT_STRING_print,
#else
Expand All @@ -29,7 +30,7 @@ asn_TYPE_operation_t asn_OP_BIT_STRING = {
0,
#endif /* !defined(ASN_DISABLE_BER_SUPPORT) */
#if !defined(ASN_DISABLE_XER_SUPPORT)
OCTET_STRING_decode_xer_binary,
BIT_STRING_decode_xer_binary_or_hex,
BIT_STRING_encode_xer,
#else
0,
Expand Down Expand Up @@ -67,8 +68,15 @@ asn_TYPE_operation_t asn_OP_BIT_STRING = {
BIT_STRING_random_fill,
#else
0,
#endif /* !defined(ASN_DISABLE_RFILL_SUPPORT) */
0 /* Use generic outmost tag fetcher */
#endif /* !defined(ASN_DISABLE_RFILL_SUPPORT) */
0 /* Use generic outmost tag fetcher */,
#if !defined(ASN_DISABLE_CBOR_SUPPORT)
BIT_STRING_decode_cbor,
BIT_STRING_encode_cbor,
#else
0,
0,
#endif /* !defined(ASN_DISABLE_CBOR_SUPPORT) */
};
asn_TYPE_descriptor_t asn_DEF_BIT_STRING = {
"BIT STRING",
Expand Down
8 changes: 6 additions & 2 deletions src/icspacket/include/skeletons/BIT_STRING.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ asn_constr_check_f BIT_STRING_constraint;
#endif /* !defined(ASN_DISABLE_BER_SUPPORT) */

#if !defined(ASN_DISABLE_XER_SUPPORT)
#define BIT_STRING_decode_xer OCTET_STRING_decode_xer_binary
#define BIT_STRING_decode_xer BIT_STRING_decode_xer_binary_or_hex
xer_type_encoder_f BIT_STRING_encode_xer;
#endif /* !defined(ASN_DISABLE_XER_SUPPORT) */

Expand All @@ -53,7 +53,11 @@ jer_type_encoder_f BIT_STRING_encode_jer;
#if !defined(ASN_DISABLE_OER_SUPPORT)
oer_type_decoder_f BIT_STRING_decode_oer;
oer_type_encoder_f BIT_STRING_encode_oer;
#endif /* !defined(ASN_DISABLE_OER_SUPPORT) */
#endif /* !defined(ASN_DISABLE_OER_SUPPORT) */
#if !defined(ASN_DISABLE_CBOR_SUPPORT)
cbor_type_decoder_f BIT_STRING_decode_cbor;
cbor_type_encoder_f BIT_STRING_encode_cbor;
#endif /* !defined(ASN_DISABLE_CBOR_SUPPORT) */

#if !defined(ASN_DISABLE_UPER_SUPPORT)
per_type_decoder_f BIT_STRING_decode_uper;
Expand Down
14 changes: 9 additions & 5 deletions src/icspacket/include/skeletons/BIT_STRING_jer.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,22 @@ asn_dec_rval_t BIT_STRING_decode_jer(const asn_codec_ctx_t *opt_codec_ctx,
void *integer_ptr = (void *)&integer;
memset(&integer, 0, sizeof(integer));

asn_dec_rval_t dec = INTEGER_decode_jer(NULL, &asn_DEF_INTEGER, NULL,
&integer_ptr, p0, p - p0);
if (dec.code == RC_OK) {
if (asn_INTEGER2ulong(&integer, (unsigned long *)&length)) {
asn_dec_rval_t dec =
INTEGER_decode_jer(NULL, &asn_DEF_INTEGER, NULL, &integer_ptr, p0, p-p0);
if(dec.code == RC_OK) {
if(asn_INTEGER2ulong(&integer, (unsigned long *)&length)) {
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &integer);
RETURN(RC_FAIL);
}
} else {
RETURN(RC_FAIL);
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_INTEGER, &integer);

if (dec.code != RC_OK) RETURN(RC_FAIL);
if(dec.code != RC_OK) RETURN(RC_FAIL);
if(length > st->size * 8 || (st->size * 8) - length > 7) {
RETURN(RC_FAIL);
}
st->bits_unused = (st->size * 8) - length;

SKIPCHAR('}');
Expand Down
Loading
Loading