diff options
Diffstat (limited to 'source/python/py_tdbpack.c')
-rw-r--r-- | source/python/py_tdbpack.c | 662 |
1 files changed, 0 insertions, 662 deletions
diff --git a/source/python/py_tdbpack.c b/source/python/py_tdbpack.c deleted file mode 100644 index e5044943be3..00000000000 --- a/source/python/py_tdbpack.c +++ /dev/null @@ -1,662 +0,0 @@ -/* -*- c-file-style: "python"; indent-tabs-mode: nil; -*- - - Python wrapper for Samba tdb pack/unpack functions - Copyright (C) Martin Pool 2002 - - - NOTE PYTHON STYLE GUIDE - http://www.python.org/peps/pep-0007.html - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - - - -#include "Python.h" - -static int pytdbpack_calc_reqd_len(char *format_str, - PyObject *val_seq); - -static PyObject *pytdbpack_unpack_item(char, - char **pbuf, - int *plen); -static int -pytdbpack_calc_item_len(char format_ch, - PyObject *val_obj); - -static PyObject *pytdbpack_pack_data(const char *format_str, - PyObject *val_seq, - unsigned char *buf); - - - -static const char * pytdbpack_docstring = -"Convert between Python values and Samba binary encodings. - -This module is conceptually similar to the standard 'struct' module, but it -uses both a different binary format and a different description string. - -Samba's encoding is based on that used inside DCE-RPC and SMB: a -little-endian, unpadded, non-self-describing binary format. It is intended -that these functions be as similar as possible to the routines in Samba's -tdb/tdbutil module, with appropriate adjustments for Python datatypes. - -Python strings are used to specify the format of data to be packed or -unpacked. - -Strings in TDBs are typically stored in DOS codepages. The caller of this -module must make appropriate translations if necessary, typically to and from -Unicode objects. - -tdbpack format strings: - - 'f': NULL-terminated string in DOS codepage - - 'P': same as 'f' - - 'd': 4 byte little-endian number - - 'w': 2 byte little-endian number - - 'P': \"Pointer\" value -- in the subset of DCERPC used by Samba, this is - really just an \"exists\" or \"does not exist\" flag. The boolean - value of the Python object is used. - - 'B': 4-byte LE length, followed by that many bytes of binary data. - Corresponds to a Python byte string of the appropriate length. - - '$': Special flag indicating that the preceding format code should be - repeated while data remains. This is only supported for unpacking. - - Every code corresponds to a single Python object, except 'B' which - corresponds to two values (length and contents), and '$', which produces - however many make sense. -"; - - -static char const pytdbpack_pack_doc[] = -"pack(format, values) -> buffer -Pack Python objects into Samba binary format according to format string. - -arguments: - format -- string of tdbpack format characters - values -- sequence of value objects corresponding 1:1 to format characters - -returns: - buffer -- string containing packed data - -raises: - IndexError -- if there are not the same number of format codes as of - values - ValueError -- if any of the format characters is illegal - TypeError -- if the format is not a string, or values is not a sequence, - or any of the values is of the wrong type for the corresponding - format character -"; - - -static char const pytdbpack_unpack_doc[] = -"unpack(format, buffer) -> (values, rest) -Unpack Samba binary data according to format string. - -arguments: - format -- string of tdbpack characters - buffer -- string of packed binary data - -returns: - 2-tuple of: - values -- sequence of values corresponding 1:1 to format characters - rest -- string containing data that was not decoded, or '' if the - whole string was consumed - -raises: - IndexError -- if there is insufficient data in the buffer for the - format (or if the data is corrupt and contains a variable-length - field extending past the end) - ValueError -- if any of the format characters is illegal - -notes: - Because unconsumed data is returned, you can feed it back in to the - unpacker to extract further fields. Alternatively, if you wish to modify - some fields near the start of the data, you may be able to save time by - only unpacking and repacking the necessary part. -"; - - - -/* - Game plan is to first of all walk through the arguments and calculate the - total length that will be required. We allocate a Python string of that - size, then walk through again and fill it in. - - We just borrow references to all the passed arguments, since none of them - need to be permanently stored. We transfer ownership to the returned - object. - */ -static PyObject * -pytdbpack_pack(PyObject *self, - PyObject *args) -{ - char *format_str; - PyObject *val_seq, *fast_seq, *buf_str; - int reqd_len; - char *packed_buf; - - /* TODO: Test passing wrong types or too many arguments */ - if (!PyArg_ParseTuple(args, "sO", &format_str, &val_seq)) - return NULL; - - /* Convert into a list or tuple (if not already one), so that we can - * index more easily. */ - fast_seq = PySequence_Fast(val_seq, - __FUNCTION__ ": argument 2 must be sequence"); - if (!fast_seq) - return NULL; - - reqd_len = pytdbpack_calc_reqd_len(format_str, fast_seq); - if (reqd_len == -1) /* exception was thrown */ - return NULL; - - /* Allocate space. - - This design causes an unnecessary copying of the data when Python - constructs an object, and that might possibly be avoided by using a - Buffer object of some kind instead. I'm not doing that for now - though. */ - packed_buf = malloc(reqd_len); - if (!packed_buf) { - PyErr_Format(PyExc_MemoryError, - "%s: couldn't allocate %d bytes for packed buffer", - __FUNCTION__, reqd_len); - return NULL; - } - - if (!pytdbpack_pack_data(format_str, fast_seq, packed_buf)) { - free(packed_buf); - return NULL; - } - - buf_str = PyString_FromStringAndSize(packed_buf, reqd_len); - free(packed_buf); /* get rid of tmp buf */ - - return buf_str; -} - - - -static PyObject * -pytdbpack_unpack(PyObject *self, - PyObject *args) -{ - char *format_str, *packed_str, *ppacked; - PyObject *val_list = NULL, *ret_tuple = NULL; - PyObject *rest_string = NULL; - int format_len, packed_len; - int i; - char last_format = '#'; - - /* get arguments */ - if (!PyArg_ParseTuple(args, "ss#", &format_str, &packed_str, &packed_len)) - return NULL; - - format_len = strlen(format_str); - - /* allocate list to hold results */ - val_list = PyList_New(format_len); - if (!val_list) - goto failed; - ret_tuple = PyTuple_New(2); - if (!ret_tuple) - goto failed; - - /* For every object, unpack. */ - for (ppacked = packed_str, i = 0; i < format_len; i++) { - PyObject *val_obj; - char format; - - format = format_str[i]; - if (format == '$') { - if (i == 0) { - PyErr_Format(PyExc_ValueError, - "%s: '$' may not be first character in format", - __FUNCTION__); - goto failed; - } - else { - format = last_format; /* repeat */ - } - } - - val_obj = pytdbpack_unpack_item(format, - &ppacked, - &packed_len); - if (!val_obj) - goto failed; - - PyList_SET_ITEM(val_list, i, val_obj); - last_format = format; - } - - /* put leftovers in box for lunch tomorrow */ - rest_string = PyString_FromStringAndSize(ppacked, packed_len); - if (!rest_string) - goto failed; - - /* return (values, rest) tuple; give up references to them */ - PyTuple_SET_ITEM(ret_tuple, 0, val_list); - val_list = NULL; - PyTuple_SET_ITEM(ret_tuple, 1, rest_string); - val_list = NULL; - return ret_tuple; - - failed: - /* handle failure: deallocate anything */ - Py_XDECREF(val_list); - Py_XDECREF(ret_tuple); - Py_XDECREF(rest_string); - return NULL; -} - - -/* - Internal routine that calculates how many bytes will be required to - encode the values in the format. - - Also checks that the value list is the right size for the format list. - - Returns number of bytes (may be 0), or -1 if there's something wrong, in - which case a Python exception has been raised. - - Arguments: - - val_seq: a Fast Sequence (list or tuple), being all the values -*/ -static int -pytdbpack_calc_reqd_len(char *format_str, - PyObject *val_seq) -{ - int len = 0; - char *p; - int val_i; - int val_len; - - val_len = PySequence_Fast_GET_SIZE(val_seq); - - for (p = format_str, val_i = 0; *p; p++, val_i++) { - char ch = *p; - PyObject *val_obj; - int item_len; - - if (val_i >= val_len) { - PyErr_Format(PyExc_IndexError, - "samba.tdbpack.pack: value list is too short for format string"); - return -1; - } - - /* borrow a reference to the item */ - val_obj = PySequence_Fast_GET_ITEM(val_seq, val_i); - if (!val_obj) - return -1; - - item_len = pytdbpack_calc_item_len(ch, val_obj); - if (item_len == -1) - return -1; - else - len += item_len; - } - - if (val_i != val_len) { - PyErr_Format(PyExc_IndexError, - "%s: value list is wrong length for format string", - __FUNCTION__); - return -1; - } - - return len; -} - - -/* - Calculate the number of bytes required to pack a single value. -*/ -static int -pytdbpack_calc_item_len(char ch, - PyObject *val_obj) -{ - if (ch == 'd' || ch == 'w') { - if (!PyInt_Check(val_obj)) { - PyErr_Format(PyExc_TypeError, - "tdbpack: format '%c' requires an Int", - ch); - return -1; - } - if (ch == 'w') - return 2; - else - return 4; - } else if (ch == 'p') { - return 4; - } - else if (ch == 'f' || ch == 'P' || ch == 'B') { - /* nul-terminated 8-bit string */ - if (!PyString_Check(val_obj)) { - PyErr_Format(PyExc_TypeError, - "tdbpack: format '%c' requires a String", - ch); - return -1; - } - - if (ch == 'B') { - /* byte buffer; just use Python string's length, plus - a preceding word */ - return 4 + PyString_GET_SIZE(val_obj); - } - else { - /* one nul character */ - return 1 + PyString_GET_SIZE(val_obj); - } - } - else { - PyErr_Format(PyExc_ValueError, - __FUNCTION__ ": format character '%c' is not supported", - ch); - - return -1; - } -} - - -/* - XXX: glib and Samba have quicker macro for doing the endianness conversions, - but I don't know of one in plain libc, and it's probably not a big deal. I - realize this is kind of dumb because we'll almost always be on x86, but - being safe is important. -*/ -static void pack_int32(unsigned long val_long, unsigned char **pbuf) -{ - (*pbuf)[0] = val_long & 0xff; - (*pbuf)[1] = (val_long >> 8) & 0xff; - (*pbuf)[2] = (val_long >> 16) & 0xff; - (*pbuf)[3] = (val_long >> 24) & 0xff; - (*pbuf) += 4; -} - - -static void pack_bytes(long len, const char *from, - unsigned char **pbuf) -{ - memcpy(*pbuf, from, len); - (*pbuf) += len; -} - - -static void -unpack_err_too_short(void) -{ - PyErr_Format(PyExc_IndexError, - __FUNCTION__ ": data too short for unpack format"); -} - - -static PyObject * -unpack_int32(char **pbuf, int *plen) -{ - long v; - unsigned char *b; - - if (*plen < 4) { - unpack_err_too_short(); - return NULL; - } - - b = *pbuf; - v = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; - - (*pbuf) += 4; - (*plen) -= 4; - - return PyInt_FromLong(v); -} - - -static PyObject *unpack_int16(char **pbuf, int *plen) -{ - long v; - unsigned char *b; - - if (*plen < 2) { - unpack_err_too_short(); - return NULL; - } - - b = *pbuf; - v = b[0] | b[1]<<8; - - (*pbuf) += 2; - (*plen) -= 2; - - return PyInt_FromLong(v); -} - - -static PyObject * -unpack_string(char **pbuf, int *plen) -{ - int len; - char *nul_ptr, *start; - - start = *pbuf; - - nul_ptr = memchr(start, '\0', *plen); - if (!nul_ptr) { - unpack_err_too_short(); - return NULL; - } - - len = nul_ptr - start; - - *pbuf += len + 1; /* skip \0 */ - *plen -= len + 1; - - return PyString_FromStringAndSize(start, len); -} - - -static PyObject * -unpack_buffer(char **pbuf, int *plen) -{ - /* first get 32-bit len */ - long slen; - unsigned char *b; - unsigned char *start; - - if (*plen < 4) { - unpack_err_too_short(); - return NULL; - } - - b = *pbuf; - slen = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; - - if (slen < 0) { /* surely you jest */ - PyErr_Format(PyExc_ValueError, - __FUNCTION__ ": buffer seems to have negative length"); - return NULL; - } - - (*pbuf) += 4; - (*plen) -= 4; - start = *pbuf; - - if (*plen < slen) { - PyErr_Format(PyExc_IndexError, - __FUNCTION__ ": not enough data to unpack buffer: " - "need %d bytes, have %d", - (int) slen, *plen); - return NULL; - } - - (*pbuf) += slen; - (*plen) -= slen; - - return PyString_FromStringAndSize(start, slen); -} - - -/* Unpack a single field from packed data, according to format character CH. - Remaining data is at *PBUF, of *PLEN. - - *PBUF is advanced, and *PLEN reduced to reflect the amount of data that has - been consumed. - - Returns a reference to the unpacked Python object, or NULL for failure. -*/ -static PyObject *pytdbpack_unpack_item(char ch, - char **pbuf, - int *plen) -{ - if (ch == 'w') { /* 16-bit int */ - return unpack_int16(pbuf, plen); - } - else if (ch == 'd' || ch == 'p') { /* 32-bit int */ - /* pointers can just come through as integers */ - return unpack_int32(pbuf, plen); - } - else if (ch == 'f' || ch == 'P') { /* nul-term string */ - return unpack_string(pbuf, plen); - } - else if (ch == 'B') { /* length, buffer */ - return unpack_buffer(pbuf, plen); - } - else { - PyErr_Format(PyExc_ValueError, - __FUNCTION__ ": format character '%c' is not supported", - ch); - - return NULL; - } -} - - - -/* - Pack a single item VAL_OBJ, encoded using format CH, into a buffer at *PBUF, - and advance the pointer. Buffer length has been pre-calculated so we are - sure that there is enough space. - -*/ -static PyObject * -pytdbpack_pack_item(char ch, - PyObject *val_obj, - unsigned char **pbuf) -{ - if (ch == 'w') { - unsigned long val_long = PyInt_AsLong(val_obj); - (*pbuf)[0] = val_long & 0xff; - (*pbuf)[1] = (val_long >> 8) & 0xff; - (*pbuf) += 2; - } - else if (ch == 'd') { - /* 4-byte LE number */ - pack_int32(PyInt_AsLong(val_obj), pbuf); - } - else if (ch == 'p') { - /* "Pointer" value -- in the subset of DCERPC used by Samba, - this is really just an "exists" or "does not exist" - flag. */ - pack_int32(PyObject_IsTrue(val_obj), pbuf); - } - else if (ch == 'f' || ch == 'P') { - int size; - char *sval; - - size = PyString_GET_SIZE(val_obj); - sval = PyString_AS_STRING(val_obj); - pack_bytes(size+1, sval, pbuf); /* include nul */ - } - else if (ch == 'B') { - int size; - char *sval; - - size = PyString_GET_SIZE(val_obj); - pack_int32(size, pbuf); - sval = PyString_AS_STRING(val_obj); - pack_bytes(size, sval, pbuf); /* do not include nul */ - } - else { - /* this ought to be caught while calculating the length, but - just in case. */ - PyErr_Format(PyExc_ValueError, - "%s: format character '%c' is not supported", - __FUNCTION__, ch); - - return NULL; - } - - return Py_None; -} - - -/* - Pack data according to FORMAT_STR from the elements of VAL_SEQ into - PACKED_BUF. - - The string has already been checked out, so we know that VAL_SEQ is large - enough to hold the packed data, and that there are enough value items. - (However, their types may not have been thoroughly checked yet.) - - In addition, val_seq is a Python Fast sequence. - - Returns NULL for error (with exception set), or None. -*/ -PyObject * -pytdbpack_pack_data(const char *format_str, - PyObject *val_seq, - unsigned char *packed_buf) -{ - int i; - - for (i = 0; format_str[i]; i++) { - char ch = format_str[i]; - PyObject *val_obj; - - /* borrow a reference to the item */ - val_obj = PySequence_Fast_GET_ITEM(val_seq, i); - if (!val_obj) - return NULL; - - if (!pytdbpack_pack_item(ch, val_obj, &packed_buf)) - return NULL; - } - - return Py_None; -} - - - - - -static PyMethodDef pytdbpack_methods[] = { - { "pack", pytdbpack_pack, METH_VARARGS, (char *) pytdbpack_pack_doc }, - { "unpack", pytdbpack_unpack, METH_VARARGS, (char *) pytdbpack_unpack_doc }, -}; - -DL_EXPORT(void) -inittdbpack(void) -{ - Py_InitModule3("tdbpack", pytdbpack_methods, - (char *) pytdbpack_docstring); -} |