From: Joseph Sutton Date: Sun, 19 Nov 2023 21:07:22 +0000 (+1300) Subject: pidl: Add new ‘u16string’ type X-Git-Tag: talloc-2.4.2~615 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f94b981e8641a6b18f72a50b6596e9275bb034e4;p=thirdparty%2Fsamba.git pidl: Add new ‘u16string’ type This type represents a UTF‐16–encoded string. These strings are kept UTF‐16–encoded rather than converted to the Unix charset to be stored in memory; this avoids issues regarding NULL termination and conversion between character sets. We want to be able to handle strings that are not valid UTF‐16. Not bumping the NDR ABI version, because there hasn’t been an NDR release since commit c4f281e9ae36c225b6003e0fa1cb8fb2e67bf543. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett --- diff --git a/librpc/ABI/ndr-4.0.0.sigs b/librpc/ABI/ndr-4.0.0.sigs index f88ca733aa7..66a1b64cf60 100644 --- a/librpc/ABI/ndr-4.0.0.sigs +++ b/librpc/ABI/ndr-4.0.0.sigs @@ -81,6 +81,7 @@ ndr_print_svcctl_ServerType: void (struct ndr_print *, const char *, uint32_t) ndr_print_time_t: void (struct ndr_print *, const char *, time_t) ndr_print_timespec: void (struct ndr_print *, const char *, const struct timespec *) ndr_print_timeval: void (struct ndr_print *, const char *, const struct timeval *) +ndr_print_u16string: void (struct ndr_print *, const char *, const uint16_t *) ndr_print_udlong: void (struct ndr_print *, const char *, uint64_t) ndr_print_udlongr: void (struct ndr_print *, const char *, uint64_t) ndr_print_uid_t: void (struct ndr_print *, const char *, uid_t) @@ -157,6 +158,7 @@ ndr_pull_time_t: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, time_t *) ndr_pull_timespec: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, struct timespec *) ndr_pull_timeval: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, struct timeval *) ndr_pull_trailer_align: enum ndr_err_code (struct ndr_pull *, size_t) +ndr_pull_u16string: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, const uint16_t **) ndr_pull_udlong: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, uint64_t *) ndr_pull_udlongr: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, uint64_t *) ndr_pull_uid_t: enum ndr_err_code (struct ndr_pull *, ndr_flags_type, uid_t *) @@ -232,6 +234,7 @@ ndr_push_time_t: enum ndr_err_code (struct ndr_push *, ndr_flags_type, time_t) ndr_push_timespec: enum ndr_err_code (struct ndr_push *, ndr_flags_type, const struct timespec *) ndr_push_timeval: enum ndr_err_code (struct ndr_push *, ndr_flags_type, const struct timeval *) ndr_push_trailer_align: enum ndr_err_code (struct ndr_push *, size_t) +ndr_push_u16string: enum ndr_err_code (struct ndr_push *, ndr_flags_type, const uint16_t *) ndr_push_udlong: enum ndr_err_code (struct ndr_push *, ndr_flags_type, uint64_t) ndr_push_udlongr: enum ndr_err_code (struct ndr_push *, ndr_flags_type, uint64_t) ndr_push_uid_t: enum ndr_err_code (struct ndr_push *, ndr_flags_type, uid_t) diff --git a/librpc/ndr/libndr.h b/librpc/ndr/libndr.h index 0ccc0d2c33d..adcbdf18c0c 100644 --- a/librpc/ndr/libndr.h +++ b/librpc/ndr/libndr.h @@ -806,6 +806,7 @@ NDR_SCALAR_PROTO(DATA_BLOB, DATA_BLOB) NDR_SCALAR_PROTO(ipv4address, const char *) NDR_SCALAR_PROTO(ipv6address, const char *) NDR_SCALAR_PROTO(string, const char *) +NDR_SCALAR_PROTO(u16string, const uint16_t *) NDR_SCALAR_PROTO(double, double) enum ndr_err_code ndr_pull_policy_handle(struct ndr_pull *ndr, ndr_flags_type ndr_flags, struct policy_handle *r); diff --git a/librpc/ndr/ndr_string.c b/librpc/ndr/ndr_string.c index 64a46655206..0d18354a5de 100644 --- a/librpc/ndr/ndr_string.c +++ b/librpc/ndr/ndr_string.c @@ -476,6 +476,138 @@ _PUBLIC_ uint32_t ndr_size_string(int ret, const char * const* string, ndr_flags return ret+strlen(*string)+1; } +/** + pull a UTF‐16 string from the wire +*/ +_PUBLIC_ enum ndr_err_code ndr_pull_u16string(struct ndr_pull *ndr, + ndr_flags_type ndr_flags, + const uint16_t **s) +{ + uint16_t *as = NULL; + const char *const src_str = (char *)ndr->data + ndr->offset; + size_t src_len = 0; + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + if (ndr->flags & LIBNDR_ENCODING_FLAGS) { + return ndr_pull_error( + ndr, + NDR_ERR_STRING, + "Unsupported string flags 0x%" PRI_LIBNDR_FLAGS + "passed to ndr_pull_u16string()\n", + ndr->flags & LIBNDR_STRING_FLAGS); + } + + switch (ndr->flags & LIBNDR_STRING_FLAGS) { + case LIBNDR_FLAG_STR_NULLTERM: + /* + * We ensure that src_len cannot equal 0 by + * requiring that there be enough bytes for at least + * the NULL terminator + */ + NDR_PULL_NEED_BYTES(ndr, 2); + src_len = utf16_null_terminated_len_n(src_str, + ndr->data_size - + ndr->offset); + break; + + default: + return ndr_pull_error( + ndr, + NDR_ERR_STRING, + "Unsupported string flags 0x%" PRI_LIBNDR_FLAGS + "passed to ndr_pull_u16string()\n", + ndr->flags & LIBNDR_STRING_FLAGS); + } + + NDR_PULL_NEED_BYTES(ndr, src_len); + as = talloc_utf16_strlendup(ndr->current_mem_ctx, + src_str, + src_len); + if (as == NULL) { + return ndr_pull_error(ndr, + NDR_ERR_ALLOC, + "Failed to talloc_utf16_strlendup() in " + "ndr_pull_u16string()"); + } + + NDR_CHECK(ndr_pull_advance(ndr, src_len)); + *s = as; + + return NDR_ERR_SUCCESS; +} + +/** + push a UTF‐16 string onto the wire +*/ +_PUBLIC_ enum ndr_err_code ndr_push_u16string(struct ndr_push *ndr, + ndr_flags_type ndr_flags, + const uint16_t *s) +{ + size_t s_len; + + if (!(ndr_flags & NDR_SCALARS)) { + return NDR_ERR_SUCCESS; + } + + if (s == NULL) { + return ndr_push_error( + ndr, + NDR_ERR_INVALID_POINTER, + "NULL pointer passed to ndr_push_u16string()"); + } + + s_len = utf16_null_terminated_len(s); + if (s_len > UINT32_MAX) { + return ndr_push_error( + ndr, + NDR_ERR_LENGTH, + "length overflow in ndr_push_u16string()"); + } + + if (ndr->flags & LIBNDR_ENCODING_FLAGS) { + return ndr_push_error( + ndr, + NDR_ERR_STRING, + "Unsupported string flags 0x%" PRI_LIBNDR_FLAGS + "passed to ndr_push_u16string()\n", + ndr->flags & LIBNDR_STRING_FLAGS); + } + + switch (ndr->flags & LIBNDR_STRING_FLAGS) { + case LIBNDR_FLAG_STR_NULLTERM: + NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)s, s_len)); + break; + + default: + if (ndr->flags & LIBNDR_FLAG_REMAINING) { + NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)s, s_len)); + break; + } + + return ndr_push_error( + ndr, + NDR_ERR_STRING, + "Unsupported string flags 0x%" PRI_LIBNDR_FLAGS + "passed to ndr_push_u16string()\n", + ndr->flags & LIBNDR_STRING_FLAGS); + } + + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ void ndr_print_u16string(struct ndr_print *ndr, + const char *name, + const uint16_t *s) +{ + return ndr_print_array_uint8(ndr, + name, + (const uint8_t *)s, + utf16_len(s)); +} + static uint32_t guess_string_array_size(struct ndr_pull *ndr, ndr_flags_type ndr_flags) { /* diff --git a/pidl/lib/Parse/Pidl/NDR.pm b/pidl/lib/Parse/Pidl/NDR.pm index 259950959bd..18db6cfe258 100644 --- a/pidl/lib/Parse/Pidl/NDR.pm +++ b/pidl/lib/Parse/Pidl/NDR.pm @@ -66,6 +66,7 @@ my $scalar_alignment = { 'udlongr' => 4, 'DATA_BLOB' => 4, 'string' => 4, + 'u16string' => 4, 'string_array' => 4, #??? 'time_t' => 4, 'uid_t' => 8, diff --git a/pidl/lib/Parse/Pidl/Samba4/Python.pm b/pidl/lib/Parse/Pidl/Samba4/Python.pm index 432f8724ba0..2f257433859 100644 --- a/pidl/lib/Parse/Pidl/Samba4/Python.pm +++ b/pidl/lib/Parse/Pidl/Samba4/Python.pm @@ -1700,6 +1700,27 @@ sub ConvertStringFromPythonData($$$$$) $self->pidl("}"); } +sub ConvertU16StringFromPythonData($$$$$) +{ + my ($self, $mem_ctx, $py_var, $target, $fail) = @_; + + $self->pidl("{"); + $self->indent; + $self->pidl("uint16_t *str = NULL;"); + $self->pidl(""); + $self->pidl("str = PyUtf16String_FromBytes("); + $self->pidl(" $mem_ctx, $py_var);"); + $self->pidl("if (str == NULL) {"); + $self->indent; + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("$target = str;"); + $self->deindent; + $self->pidl("}"); +} + sub ConvertObjectFromPythonData($$$$$$;$$) { my ($self, $mem_ctx, $cvar, $ctype, $target, $fail, $location, $switch) = @_; @@ -1854,6 +1875,12 @@ sub ConvertObjectFromPythonData($$$$$$;$$) return; } + if ($actual_ctype->{TYPE} eq "SCALAR" and + $actual_ctype->{NAME} eq "u16string") { + $self->ConvertU16StringFromPythonData($mem_ctx, $cvar, $target, $fail); + return; + } + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "NTSTATUS") { $self->pidl("$target = NT_STATUS(PyLong_AsLong($cvar));"); return; @@ -2086,6 +2113,10 @@ sub ConvertScalarToPython($$$$) return "PyString_FromStringOrNULL($cvar)"; } + if ($ctypename eq "u16string") { + return "PyBytes_FromUtf16StringOrNULL($cvar)"; + } + # Not yet supported if ($ctypename eq "string_array") { return "pytalloc_GenericObject_reference_ex($mem_ctx, $cvar)"; diff --git a/pidl/lib/Parse/Pidl/Typelist.pm b/pidl/lib/Parse/Pidl/Typelist.pm index a0e92a49cf7..09d416f7b20 100644 --- a/pidl/lib/Parse/Pidl/Typelist.pm +++ b/pidl/lib/Parse/Pidl/Typelist.pm @@ -24,12 +24,13 @@ my %types = (); my @reference_scalars = ( "string", "string_array", "nbt_string", "dns_string", "wrepl_nbt_name", "dnsp_name", "dnsp_string", - "ipv4address", "ipv6address" + "ipv4address", "ipv6address", "u16string" ); my @non_fixed_size_scalars = ( "string", "string_array", "nbt_string", "dns_string", - "wrepl_nbt_name", "dnsp_name", "dnsp_string" + "wrepl_nbt_name", "dnsp_name", "dnsp_string", + "u16string" ); # a list of known scalar types @@ -55,6 +56,7 @@ my %scalars = ( "pointer" => "void*", "DATA_BLOB" => "DATA_BLOB", "string" => "const char *", + "u16string" => "const uint16_t *", "string_array" => "const char **", "time_t" => "time_t", "uid_t" => "uid_t", @@ -235,7 +237,7 @@ sub is_string_type { my ($t) = @_; - return ($t eq "string"); + return ($t eq "string" or $t eq "u16string"); } sub RegisterScalars() diff --git a/pidl/tests/typelist.pl b/pidl/tests/typelist.pl index 5fc3ca25e06..e012c8060df 100755 --- a/pidl/tests/typelist.pl +++ b/pidl/tests/typelist.pl @@ -4,7 +4,7 @@ use strict; use warnings; -use Test::More tests => 57; +use Test::More tests => 58; use FindBin qw($RealBin); use lib "$RealBin"; use Util; @@ -67,6 +67,7 @@ is(1, is_scalar({TYPE => "TYPEDEF", DATA => {TYPE => "ENUM" }})); is(1, is_scalar("mytypedef")); is(1, scalar_is_reference("string")); +is(1, scalar_is_reference("u16string")); is(0, scalar_is_reference("uint32")); is(0, scalar_is_reference({TYPE => "STRUCT", NAME => "echo_foobar"}));