diff options
Diffstat (limited to 'source3/python/py_tdbpack.c')
-rw-r--r-- | source3/python/py_tdbpack.c | 408 |
1 files changed, 222 insertions, 186 deletions
diff --git a/source3/python/py_tdbpack.c b/source3/python/py_tdbpack.c index 06aebe61eb..87cd804ed4 100644 --- a/source3/python/py_tdbpack.c +++ b/source3/python/py_tdbpack.c @@ -30,19 +30,19 @@ 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_unpack_item(char, char **pbuf, int *plen, PyObject *); static PyObject *pytdbpack_pack_data(const char *format_str, PyObject *val_seq, unsigned char *buf); - + + +static PyObject *pytdbpack_bad_type(char ch, + const char *expected, + PyObject *val_obj); + static const char * pytdbpack_docstring = "Convert between Python values and Samba binary encodings. @@ -67,16 +67,17 @@ tdbpack format strings: 'P': same as 'f' - 'd': 4 byte little-endian number + 'd': 4 byte little-endian unsigned number - 'w': 2 byte little-endian number + 'w': 2 byte little-endian unsigned 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. + Corresponds to a Python integer giving the length, followed by a 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. @@ -99,12 +100,15 @@ returns: buffer -- string containing packed data raises: - IndexError -- if there are not the same number of format codes as of - values + IndexError -- if there are too few values for the format 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 + +notes: + For historical reasons, it is not an error to pass more values than are consumed + by the format. "; @@ -205,8 +209,8 @@ pytdbpack_unpack(PyObject *self, PyObject *val_list = NULL, *ret_tuple = NULL; PyObject *rest_string = NULL; int format_len, packed_len; + char last_format = '#'; /* invalid */ int i; - char last_format = '#'; /* get arguments */ if (!PyArg_ParseTuple(args, "ss#", &format_str, &packed_str, &packed_len)) @@ -214,8 +218,9 @@ pytdbpack_unpack(PyObject *self, format_len = strlen(format_str); - /* allocate list to hold results */ - val_list = PyList_New(format_len); + /* Allocate list to hold results. Initially empty, and we append + results as we go along. */ + val_list = PyList_New(0); if (!val_list) goto failed; ret_tuple = PyTuple_New(2); @@ -223,34 +228,27 @@ pytdbpack_unpack(PyObject *self, 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) + for (ppacked = packed_str, i = 0; i < format_len && format_str[i] != '$'; i++) { + last_format = format_str[i]; + /* packed_len is reduced in place */ + if (!pytdbpack_unpack_item(format_str[i], &ppacked, &packed_len, val_list)) goto failed; - - PyList_SET_ITEM(val_list, i, val_obj); - last_format = format; } - /* put leftovers in box for lunch tomorrow */ + /* If the last character was '$', keep going until out of space */ + if (format_str[i] == '$') { + if (i == 0) { + PyErr_Format(PyExc_ValueError, + "%s: '$' may not be first character in format", + __FUNCTION__); + return NULL; + } + while (packed_len > 0) + if (!pytdbpack_unpack_item(last_format, &ppacked, &packed_len, val_list)) + goto failed; + } + + /* save leftovers for next time */ rest_string = PyString_FromStringAndSize(ppacked, packed_len); if (!rest_string) goto failed; @@ -263,7 +261,8 @@ pytdbpack_unpack(PyObject *self, return ret_tuple; failed: - /* handle failure: deallocate anything */ + /* handle failure: deallocate anything. XDECREF forms handle NULL + pointers for objects that haven't been allocated yet. */ Py_XDECREF(val_list); Py_XDECREF(ret_tuple); Py_XDECREF(rest_string); @@ -293,36 +292,71 @@ pytdbpack_calc_reqd_len(char *format_str, int val_i; int val_len; - val_len = PySequence_Fast_GET_SIZE(val_seq); + val_len = PySequence_Length(val_seq); + if (val_len == -1) + return -1; 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"); + "%s: value list is too short for format string", + __FUNCTION__); return -1; } /* borrow a reference to the item */ - val_obj = PySequence_Fast_GET_ITEM(val_seq, val_i); - if (!val_obj) - return -1; + if (ch == 'd' || ch == 'p') + len += 4; + else if (ch == 'w') + len += 2; + else if (ch == 'f' || ch == 'P') { + /* nul-terminated 8-bit string */ + int item_len; + PyObject *str_obj; + + str_obj = PySequence_GetItem(val_seq, val_i); + if (!str_obj) + return -1; + + if (!PyString_Check(str_obj) || ((item_len = PyString_Size(str_obj)) == -1)) { + pytdbpack_bad_type(ch, "String", str_obj); + return -1; + } + + len += 1 + item_len; + } + else if (ch == 'B') { + /* length-preceded byte buffer: n bytes, plus a preceding + * word */ + PyObject *len_obj; + long len_val; + + len_obj = PySequence_GetItem(val_seq, val_i); + val_i++; /* skip over buffer */ + + if (!PyNumber_Check(len_obj)) { + pytdbpack_bad_type(ch, "Number", len_obj); + return -1; + } - item_len = pytdbpack_calc_item_len(ch, val_obj); - if (item_len == -1) - return -1; - else - len += item_len; - } + len_val = PyInt_AsLong(len_obj); + if (len_val < 0) { + PyErr_Format(PyExc_ValueError, + "%s: format 'B' requires positive integer", __FUNCTION__); + return -1; + } - if (val_i != val_len) { - PyErr_Format(PyExc_IndexError, - "%s: value list is wrong length for format string", - __FUNCTION__); - return -1; + len += 4 + len_val; + } + else { + PyErr_Format(PyExc_ValueError, + "%s: format character '%c' is not supported", + __FUNCTION__, ch); + + return -1; + } } return len; @@ -345,61 +379,12 @@ static PyObject *pytdbpack_bad_type(char ch, /* - * Calculate the number of bytes required to pack a single value. While doing - * this, also conduct some initial checks that the argument types are - * reasonable. - * - * Returns -1 on exception. - */ -static int -pytdbpack_calc_item_len(char ch, - PyObject *val_obj) -{ - if (ch == 'd' || ch == 'w') { - if (!PyInt_Check(val_obj)) { - pytdbpack_bad_type(ch, "Int", val_obj); - 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)) { - pytdbpack_bad_type(ch, "String", val_obj); - } - - 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, - "tdbpack: 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) +static void pack_uint32(unsigned long val_long, unsigned char **pbuf) { (*pbuf)[0] = val_long & 0xff; (*pbuf)[1] = (val_long >> 8) & 0xff; @@ -426,9 +411,9 @@ unpack_err_too_short(void) static PyObject * -unpack_int32(char **pbuf, int *plen) +unpack_uint32(char **pbuf, int *plen) { - long v; + unsigned long v; unsigned char *b; if (*plen < 4) { @@ -442,7 +427,7 @@ unpack_int32(char **pbuf, int *plen) (*pbuf) += 4; (*plen) -= 4; - return PyInt_FromLong(v); + return PyLong_FromUnsignedLong(v); } @@ -490,12 +475,13 @@ unpack_string(char **pbuf, int *plen) static PyObject * -unpack_buffer(char **pbuf, int *plen) +unpack_buffer(char **pbuf, int *plen, PyObject *val_list) { /* first get 32-bit len */ long slen; unsigned char *b; unsigned char *start; + PyObject *str_obj = NULL, *len_obj = NULL; if (*plen < 4) { unpack_err_too_short(); @@ -526,7 +512,24 @@ unpack_buffer(char **pbuf, int *plen) (*pbuf) += slen; (*plen) -= slen; - return PyString_FromStringAndSize(start, slen); + if (!(len_obj = PyInt_FromLong(slen))) + goto failed; + + if (PyList_Append(val_list, len_obj) == -1) + goto failed; + + if (!(str_obj = PyString_FromStringAndSize(start, slen))) + goto failed; + + if (PyList_Append(val_list, str_obj) == -1) + goto failed; + + return val_list; + + failed: + Py_XDECREF(len_obj); /* handles NULL */ + Py_XDECREF(str_obj); + return NULL; } @@ -536,24 +539,27 @@ unpack_buffer(char **pbuf, int *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. + Returns a reference to None, or NULL for failure. */ static PyObject *pytdbpack_unpack_item(char ch, char **pbuf, - int *plen) + int *plen, + PyObject *val_list) { + PyObject *result; + if (ch == 'w') { /* 16-bit int */ - return unpack_int16(pbuf, plen); + result = 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); + result = unpack_uint32(pbuf, plen); } else if (ch == 'f' || ch == 'P') { /* nul-term string */ - return unpack_string(pbuf, plen); + result = unpack_string(pbuf, plen); } else if (ch == 'B') { /* length, buffer */ - return unpack_buffer(pbuf, plen); + return unpack_buffer(pbuf, plen, val_list); } else { PyErr_Format(PyExc_ValueError, @@ -562,68 +568,19 @@ static PyObject *pytdbpack_unpack_item(char 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); - + /* otherwise OK */ + if (!result) return NULL; - } - - return Py_None; + if (PyList_Append(val_list, result) == -1) + return NULL; + + return val_list; } + + /* Pack data according to FORMAT_STR from the elements of VAL_SEQ into PACKED_BUF. @@ -639,30 +596,109 @@ pytdbpack_pack_item(char ch, PyObject * pytdbpack_pack_data(const char *format_str, PyObject *val_seq, - unsigned char *packed_buf) + unsigned char *packed) { - int i; + int format_i, val_i = 0; - for (i = 0; format_str[i]; i++) { - char ch = format_str[i]; + for (format_i = 0, val_i = 0; format_str[format_i]; format_i++) { + char ch = format_str[format_i]; PyObject *val_obj; /* borrow a reference to the item */ - val_obj = PySequence_Fast_GET_ITEM(val_seq, i); + val_obj = PySequence_GetItem(val_seq, val_i++); if (!val_obj) return NULL; - if (!pytdbpack_pack_item(ch, val_obj, &packed_buf)) + if (ch == 'w') { + unsigned long val_long; + PyObject *long_obj; + + if (!(long_obj = PyNumber_Long(val_obj))) { + pytdbpack_bad_type(ch, "Long", val_obj); + return NULL; + } + + val_long = PyLong_AsUnsignedLong(long_obj); + (packed)[0] = val_long & 0xff; + (packed)[1] = (val_long >> 8) & 0xff; + (packed) += 2; + Py_DECREF(long_obj); + } + else if (ch == 'd') { + /* 4-byte LE number */ + PyObject *long_obj; + + if (!(long_obj = PyNumber_Long(val_obj))) { + pytdbpack_bad_type(ch, "Long", val_obj); + return NULL; + } + + pack_uint32(PyLong_AsUnsignedLong(long_obj), &packed); + + Py_DECREF(long_obj); + } + 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_uint32(PyObject_IsTrue(val_obj), &packed); + } + else if (ch == 'f' || ch == 'P') { + int size; + char *sval; + + size = PySequence_Length(val_obj); + if (size < 0) + return NULL; + sval = PyString_AsString(val_obj); + if (!sval) + return NULL; + pack_bytes(size+1, sval, &packed); /* include nul */ + } + else if (ch == 'B') { + long size; + char *sval; + + if (!PyNumber_Check(val_obj)) { + pytdbpack_bad_type(ch, "Number", val_obj); + return NULL; + } + + if (!(val_obj = PyNumber_Long(val_obj))) + return NULL; + + size = PyLong_AsLong(val_obj); + pack_uint32(size, &packed); + + /* Release the new reference created by the cast */ + Py_DECREF(val_obj); + + val_obj = PySequence_GetItem(val_seq, val_i++); + if (!val_obj) + return NULL; + + sval = PyString_AsString(val_obj); + if (!sval) + return NULL; + + pack_bytes(size, sval, &packed); /* 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; } - - static PyMethodDef pytdbpack_methods[] = { { "pack", pytdbpack_pack, METH_VARARGS, (char *) pytdbpack_pack_doc }, { "unpack", pytdbpack_unpack, METH_VARARGS, (char *) pytdbpack_unpack_doc }, |