aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1/c_src/asn1_erl_driver.c
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/asn1/c_src/asn1_erl_driver.c
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/asn1/c_src/asn1_erl_driver.c')
-rw-r--r--lib/asn1/c_src/asn1_erl_driver.c1667
1 files changed, 1667 insertions, 0 deletions
diff --git a/lib/asn1/c_src/asn1_erl_driver.c b/lib/asn1/c_src/asn1_erl_driver.c
new file mode 100644
index 0000000000..cd2e63a363
--- /dev/null
+++ b/lib/asn1/c_src/asn1_erl_driver.c
@@ -0,0 +1,1667 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "erl_driver.h"
+#include "ei.h"
+
+
+/* #define ASN1_DEBUG 1 */
+
+#define ASN1_OK 0
+#define ASN1_ERROR -1
+#define ASN1_COMPL_ERROR 1
+#define ASN1_MEMORY_ERROR 0
+#define ASN1_DECODE_ERROR 2
+#define ASN1_TAG_ERROR -3
+#define ASN1_LEN_ERROR -4
+#define ASN1_INDEF_LEN_ERROR -5
+#define ASN1_VALUE_ERROR -6
+
+
+#define ASN1_CLASS 0xc0
+#define ASN1_FORM 0x20
+#define ASN1_CLASSFORM (ASN1_CLASS | ASN1_FORM)
+#define ASN1_TAG 0x1f
+#define ASN1_LONG_TAG 0x7f
+
+#define ASN1_INDEFINITE_LENGTH 0x80
+#define ASN1_SHORT_DEFINITE_LENGTH 0
+
+#define ASN1_PRIMITIVE 0
+#define ASN1_CONSTRUCTED 0x20
+
+#define ASN1_COMPLETE 1
+#define ASN1_BER_TLV_DECODE 2
+#define ASN1_BER_TLV_PARTIAL_DECODE 3
+
+#define ASN1_NOVALUE 0
+
+#define ASN1_SKIPPED 0
+#define ASN1_OPTIONAL 1
+#define ASN1_CHOOSEN 2
+
+
+#define CEIL(X,Y) ((X-1) / Y + 1)
+
+#define INVMASK(X,M) (X & (M ^ 0xff))
+#define MASK(X,M) (X & M)
+
+typedef struct {
+ ErlDrvPort port;
+ int buffer_size;
+} asn1_data;
+
+/* int min_alloc_bytes; */
+
+
+static ErlDrvData asn1_drv_start(ErlDrvPort, char *);
+
+static void asn1_drv_stop(ErlDrvData);
+
+int asn1_drv_control(ErlDrvData, unsigned int, char *, int, char **, int);
+
+int complete(ErlDrvBinary **,unsigned char *,unsigned char *, int);
+
+int insert_octets(int, unsigned char **, unsigned char **, int *);
+
+int insert_octets_except_unused(int, unsigned char **, unsigned char **,
+ int *, int);
+
+int insert_octets_as_bits_exact_len(int, int, unsigned char **,
+ unsigned char **, int *);
+
+int insert_octets_as_bits(int, unsigned char **, unsigned char **,int *);
+
+int pad_bits(int, unsigned char **, int *);
+
+int insert_least_sign_bits(int, unsigned char, unsigned char **, int *);
+
+int insert_most_sign_bits(int, unsigned char, unsigned char **, int *);
+
+int insert_bits_as_bits(int, int, unsigned char **, unsigned char **, int *);
+
+int insert_octets_unaligned(int, unsigned char **, unsigned char **, int);
+
+int realloc_memory(ErlDrvBinary **,int,unsigned char **,unsigned char **);
+
+int decode_begin(ErlDrvBinary **,unsigned char *, int, unsigned int *);
+
+int decode(ErlDrvBinary **,int *,unsigned char *,int *, int);
+
+int decode_tag(char *,int *,unsigned char *,int,int *);
+
+int decode_value(int *,unsigned char *,int *,ErlDrvBinary **,int ,int);
+
+
+/* declaration of functions used for partial decode of a BER encoded
+ message */
+
+int decode_partial(ErlDrvBinary **,unsigned char *, int);
+
+int skip_tag(unsigned char *,int *,int);
+
+int skip_length_and_value(unsigned char *,int *,int);
+
+int get_tag(unsigned char *,int *,int);
+
+int get_value(char *,unsigned char *,int *,int);
+
+static ErlDrvEntry asn1_drv_entry = {
+ NULL, /* init, always NULL for dynamic drivers */
+ asn1_drv_start, /* start, called when port is opened */
+ asn1_drv_stop, /* stop, called when port is closed */
+ NULL, /* output, called when erlang has sent */
+ NULL, /* ready_input, called when input descriptor ready */
+ NULL, /* ready_output, called when output descriptor ready */
+ "asn1_erl_drv", /* char *driver_name, the argument to open_port */
+ NULL, /* finish, called when unloaded */
+ NULL, /* void * that is not used (BC) */
+ asn1_drv_control, /* control, port_control callback */
+ NULL, /* timeout, called on timeouts */
+ NULL, /* outputv, vector output interface */
+
+ NULL, /* ready_async */
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL, /* handle2 */
+ NULL /* process_exit */
+};
+
+
+
+DRIVER_INIT(asn1_erl_drv) /* must match name in driver_entry */
+{
+ return &asn1_drv_entry;
+}
+
+static ErlDrvData asn1_drv_start(ErlDrvPort port, char *buff)
+{
+ /* extern int min_alloc_bytes; */
+ char *ptr;
+ asn1_data* d;
+
+ d = (asn1_data*)driver_alloc(sizeof(asn1_data));
+ set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
+ d->port = port;
+
+ if ((ptr = getenv("ASN1_MIN_BUF_SIZE")) == NULL)
+ d->buffer_size = 1024;
+ else
+ d->buffer_size = atoi(ptr);
+ return (ErlDrvData)d;
+}
+
+
+static void asn1_drv_stop(ErlDrvData handle)
+{
+ driver_free((char*)handle);
+}
+
+
+
+int asn1_drv_control(ErlDrvData handle,
+ unsigned int command,
+ char *buf,
+ int buf_len,
+ char **res_buf,
+ int res_buf_len)
+{
+ char *complete_buf;
+ int complete_len, decode_len;
+ ErlDrvBinary *drv_binary;
+ ErlDrvBinary **drv_bin_ptr;
+ asn1_data* a_data;
+ int min_alloc_bytes;
+ unsigned int err_pos = 0; /* in case of error, return last correct position */
+ int ret_err; /* return value in case of error in TLV decode, i.e. length of list in res_buf */
+
+ /* In case previous call to asn1_drv_control resulted in a change of
+ return value from binary to integer list */
+ a_data = (asn1_data *)handle;
+ min_alloc_bytes = a_data->buffer_size;
+ set_port_control_flags(a_data->port, PORT_CONTROL_FLAG_BINARY);
+
+ if (command == ASN1_COMPLETE)
+ { /* Do the PER complete encode step */
+ if ((drv_binary = driver_alloc_binary(buf_len))==NULL) {
+ /* error handling */
+ set_port_control_flags(a_data->port, 0);
+ return ASN1_MEMORY_ERROR;
+ }
+ complete_buf = drv_binary->orig_bytes;
+ if ((complete_len = complete(&drv_binary,complete_buf,buf,buf_len)) == ASN1_ERROR)
+ {
+ /* error handling due to failure in complete */
+ /* printf("error when running complete\n\r"); */
+ driver_free_binary(drv_binary);
+ set_port_control_flags(a_data->port, 0);
+ **res_buf = '1';
+ return ASN1_COMPL_ERROR;
+ }
+ /* printf("complete_len=%dbuf_len=%d,orig_size=%d\n\r",complete_len,buf_len,drv_binary->orig_size); */
+ /* now the message is complete packed, return to Erlang */
+ /* if (complete_len < buf_len) {*/
+ if (complete_len < drv_binary->orig_size) {
+ ErlDrvBinary *tmp;
+ if ((tmp=driver_realloc_binary(drv_binary,complete_len)) == NULL){
+ /*error handling due to memory allocation failure */
+ driver_free_binary(drv_binary);
+ set_port_control_flags(a_data->port, 0);
+ return ASN1_MEMORY_ERROR;
+ }else
+ drv_binary=tmp;
+ }
+ *res_buf = (char *)drv_binary;
+ return complete_len;
+ } else if (command == ASN1_BER_TLV_DECODE) { /* control == 2 */
+ /* Do the tlv decode,
+ return the resulting term encoded on the Erlang
+ external format */
+/* printf("driver: buffer_len = %d, min_alloc_bytes = %d\r\n",buf_len,min_alloc_bytes); */
+ if ((drv_binary = driver_alloc_binary((buf_len*5)+min_alloc_bytes))==NULL) {
+ /* error handling */
+ set_port_control_flags(a_data->port, 0);
+ return ASN1_MEMORY_ERROR;
+ }
+ drv_bin_ptr = &drv_binary;
+ if ((decode_len = decode_begin(drv_bin_ptr,buf,buf_len,&err_pos)) <= ASN1_ERROR)
+ {
+ /* error handling due to failure in decode */
+ char tmp_res_buf[5];
+ driver_free_binary(*drv_bin_ptr);
+ set_port_control_flags(a_data->port, 0);
+
+ if(decode_len==ASN1_ERROR)
+ tmp_res_buf[0]='1';
+ else if(decode_len==ASN1_TAG_ERROR)
+ tmp_res_buf[0]='2';
+ else if(decode_len==ASN1_LEN_ERROR)
+ tmp_res_buf[0]='3';
+ else if(decode_len==ASN1_INDEF_LEN_ERROR)
+ tmp_res_buf[0]='4';
+ else if(decode_len==ASN1_VALUE_ERROR)
+ tmp_res_buf[0]='5';
+/* printf("err_pos=%d\r\n",err_pos); */
+/* printf("decode_len:%d\r\n",decode_len); */
+ ret_err = 1;
+ while(err_pos>0){
+ tmp_res_buf[ret_err] =(char)err_pos;/* c;*/
+ err_pos = err_pos >> 8;
+ ret_err++;
+ }
+ strncpy(*res_buf,tmp_res_buf,ret_err);
+ return ret_err;
+ }
+/* printf("decode_len=%d\r\n",decode_len); */
+ if (decode_len < ((buf_len * 5) + min_alloc_bytes)) {
+ /* not all memory was used => we have to reallocate */
+ ErlDrvBinary *tmp;
+ if ((tmp=driver_realloc_binary(*drv_bin_ptr,decode_len)) == NULL){
+ /*error handling due to memory allocation failure */
+ driver_free_binary(*drv_bin_ptr);
+ set_port_control_flags(a_data->port, 0);
+ return ASN1_MEMORY_ERROR;
+ }else
+ *drv_bin_ptr=tmp;
+ }
+ *res_buf = (char *)(*drv_bin_ptr);
+ return decode_len;
+ } else { /*command == ASN1_BER_TLV_PARTIAL_DECODE */
+ if ((drv_binary = driver_alloc_binary(buf_len))==NULL) {
+ /* error handling */
+ set_port_control_flags(a_data->port, 0);
+ return ASN1_MEMORY_ERROR;
+ }
+ drv_bin_ptr = &drv_binary;
+ if ((decode_len = decode_partial(drv_bin_ptr,buf,buf_len))
+ <= ASN1_ERROR) {
+ /* error handling due to failure in decode */
+ driver_free_binary(*drv_bin_ptr);
+ set_port_control_flags(a_data->port, 0);
+
+/* printf("asn1_drv_control 1: decode_len=%d\r\n",decode_len); */
+
+ if(decode_len==ASN1_ERROR)
+ **res_buf = '1';
+ return ASN1_DECODE_ERROR;
+ }
+ if (decode_len < buf_len) {
+ /* not all memory was used => we have to reallocate */
+ ErlDrvBinary *tmp;
+/* printf("asn1_drv_control 2: decode_len=%d\r\n",decode_len); */
+ if ((tmp=driver_realloc_binary(*drv_bin_ptr,decode_len)) == NULL){
+ /*error handling due to memory allocation failure */
+ driver_free_binary(*drv_bin_ptr);
+ set_port_control_flags(a_data->port, 0);
+ return ASN1_MEMORY_ERROR;
+ }else
+ *drv_bin_ptr=tmp;
+ }
+ *res_buf = (char *)(*drv_bin_ptr);
+ return decode_len;
+ }
+}
+
+
+
+/*
+ *
+ * This section defines functionality for the complete encode of a
+ * PER encoded message
+ *
+ */
+
+int complete(ErlDrvBinary **drv_binary,unsigned char *complete_buf,
+ unsigned char *in_buf, int in_buf_len)
+{
+ int counter = in_buf_len;
+ /* counter keeps track of number of bytes left in the
+ input buffer */
+
+ int buf_space = in_buf_len;
+ /* This is the amount of allocated space left of the complete_buf. It
+ is possible when padding is applied that more space is needed than
+ was originally allocated. */
+
+ int buf_size = in_buf_len;
+ /* Size of the buffer. May become reallocated and thus other than
+ in_buf_len */
+
+ unsigned char *in_ptr, *ptr;
+ /* in_ptr points at the next byte in in_buf to be moved to
+ complete_buf.
+ ptr points into the new completed buffer, complete_buf, at the
+ position of the next byte that will be set */
+ int unused = 8;
+ /* unused = [1,...,8] indicates how many of the rigthmost bits of
+ the byte that ptr points at that are unassigned */
+
+ int no_bits,no_bytes,in_unused,desired_len,ret, saved_mem, needed, pad_bits;
+
+ unsigned char val;
+
+ in_ptr = in_buf;
+ ptr = complete_buf;
+ *ptr = 0x00;
+ while(counter > 0) {
+ counter--;
+/* printf("*in_ptr = %d\n\r",*in_ptr); */
+ switch (*in_ptr) {
+ case 0:
+ /* just one zero-bit should be added to the buffer */
+ if(unused == 1){
+ unused = 8;
+ *++ptr = 0x00;
+ buf_space--;
+ } else
+ unused--;
+ break;
+
+ case 1:
+ /* one one-bit should be added to the buffer */
+ if(unused == 1){
+ *ptr = *ptr | 1;
+ unused = 8;
+ *++ptr = 0x00;
+ buf_space--;
+ } else {
+ *ptr = *ptr | (1 << (unused - 1));
+ unused--;
+ }
+ break;
+
+ case 2:
+ /* align buffer to end of byte */
+ if (unused != 8) {
+ *++ptr = 0x00;
+ buf_space--;
+ unused = 8;
+ }
+ break;
+
+ case 10:
+ /* next byte in in_buf tells how many bits in the second next
+ byte that will be used */
+ /* The leftmost unused bits in the value byte are supposed to be
+ zero bits */
+ no_bits = (int)*(++in_ptr);
+ val = *(++in_ptr);
+ counter -= 2;
+ if ((ret=insert_least_sign_bits(no_bits,val,&ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 20:
+ /* in this case the next value in_ptr points at holds the number
+ of following bytes that holds the value that will be inserted
+ in the completed buffer */
+ no_bytes = (int)*(++in_ptr);
+ counter -= (no_bytes + 1);
+ if ((counter<0) ||
+ (ret=insert_octets(no_bytes,&in_ptr,&ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 21:
+ /* in this case the next two bytes in_ptr points at holds the number
+ of following bytes that holds the value that will be inserted
+ in the completed buffer */
+ no_bytes = (int)*(++in_ptr);
+ no_bytes = no_bytes << 8;
+ no_bytes = no_bytes | (int)*(++in_ptr);
+ counter -= (2 + no_bytes);
+ if ((counter<0) ||
+ (ret=insert_octets(no_bytes,&in_ptr,&ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 30:
+ /* If we call the following bytes, in the buffer in_ptr points at,
+ By1,By2,Rest then Rest is the value that will be transfered to
+ the completed buffer. By1 tells how many of the rightmost bits in
+ Rest that should not be used. By2 is the length of Rest in bytes.*/
+ in_unused = (int)*(++in_ptr);
+ no_bytes = (int)*(++in_ptr);
+ counter -= (2 + no_bytes);
+/* printf("%d: case 30: in_unused=%d, no_bytes=%d,counter=%d\n\r",__LINE__,in_unused,no_bytes,counter); */
+ ret = -4711;
+ if ((counter<0) ||
+ (ret=insert_octets_except_unused(no_bytes,&in_ptr,&ptr,&unused,in_unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+/* printf("%d: ret=%d\n\r",__LINE__, ret); */
+ buf_space -= ret;
+ break;
+
+ case 31:
+ /* If we call the following bytes, in the buffer in_ptr points at,
+ By1,By2,By3,Rest then Rest is the value that will be transfered to
+ the completed buffer. By1 tells how many of the rightmost bits in
+ Rest that should not be used. By2 and By3 is the length of
+ Rest in bytes.*/
+ in_unused = (int)*(++in_ptr);
+ no_bytes = (int)*(++in_ptr);
+ no_bytes = no_bytes << 8;
+ no_bytes = no_bytes | (int)*(++in_ptr);
+ counter -= (3 + no_bytes);
+ if ((counter<0) ||
+ (ret=insert_octets_except_unused(no_bytes,&in_ptr,&ptr,&unused,in_unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 40:
+ /* This case implies that next byte,By1,(..,By1,By2,Bin,...)
+ is the desired length of the completed value, maybe needs
+ padding zero bits or removal of trailing zero bits from Bin.
+ By2 is the length of Bin and Bin is the value that will be
+ put into the completed buffer. Each byte in Bin has the value
+ 1 or 0.*/
+ desired_len = (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+
+ /* This is the algorithm for need of memory reallocation:
+ Only when padding (cases 40 - 43,45 - 47) more memory may be
+ used than allocated. Therefore one has to keep track of how
+ much of the allocated memory that has been saved, i.e. the
+ difference between the number of parsed bytes of the input buffer
+ and the number of used bytes of the output buffer.
+ If saved memory is less than needed for the padding then we
+ need more memory. */
+ saved_mem = buf_space - counter;
+ pad_bits = desired_len - no_bytes - unused;
+ needed = (pad_bits > 0) ? CEIL(pad_bits,8) : 0;
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (2 + no_bytes);
+ if ((counter<0) ||
+ (ret=insert_octets_as_bits_exact_len(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 41:
+ /* Same as case 40 apart from By2, the length of Bin, which is in
+ two bytes*/
+ desired_len = (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+ no_bytes = no_bytes << 8;
+ no_bytes = no_bytes | (int)*(++in_ptr);
+
+ saved_mem = buf_space - counter;
+ needed = CEIL((desired_len-unused),8) - no_bytes;
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (3 + no_bytes);
+ if ((counter<0) ||
+ (ret=insert_octets_as_bits_exact_len(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 42:
+ /* Same as case 40 apart from By1, the desired length, which is in
+ two bytes*/
+ desired_len = (int)*(++in_ptr);
+ desired_len = desired_len << 8;
+ desired_len = desired_len | (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+
+ saved_mem = buf_space - counter;
+ needed = CEIL((desired_len-unused),8) - no_bytes;
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (3 + no_bytes);
+ if ((counter<0) ||
+ (ret=insert_octets_as_bits_exact_len(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 43:
+ /* Same as case 40 apart from By1 and By2, the desired length and
+ the length of Bin, which are in two bytes each. */
+ desired_len = (int)*(++in_ptr);
+ desired_len = desired_len << 8;
+ desired_len = desired_len | (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+ no_bytes = no_bytes << 8;
+ no_bytes = no_bytes | (int)*(++in_ptr);
+
+ saved_mem = buf_space - counter;
+ needed = CEIL((desired_len-unused),8) - no_bytes;
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (4 + no_bytes);
+ if ((counter<0) ||
+ (ret=insert_octets_as_bits_exact_len(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 45:
+ /* This case assumes that the following bytes in the incoming buffer
+ (called By1,By2,Bin) is By1, which is the number of bits (n) that
+ will be inserted in the completed buffer. By2 is the number of
+ bytes in Bin. Each bit in the buffer Bin should be inserted from
+ the leftmost until the nth.*/
+ desired_len = (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+
+ saved_mem = buf_space - counter;
+ needed = CEIL((desired_len-unused),8) - no_bytes;
+/* printf("buf_space=%d, counter=%d, needed=%d",buf_space,counter,needed); */
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (2 + no_bytes);
+/* printf("calling insert_bits_as_bits: desired_len=%d, no_bytes=%d\n\r",desired_len,no_bytes); */
+/* printf("1in_ptr=%d\n\r",in_ptr); */
+
+ if((counter<0) ||
+ (ret=insert_bits_as_bits(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+/* printf("2in_ptr=%d, ptr=%d, complete_buf=%d\n\r",in_ptr,ptr,complete_buf); */
+/* printf("buf_space=%d, ret=%d, counter=%d\n\r",buf_space,ret,counter); */
+ buf_space -= ret;
+ break;
+
+ case 46:
+ /* Same as case 45 apart from By1, the desired length, which is
+ in two bytes. */
+ desired_len = (int)*(++in_ptr);
+ desired_len = desired_len << 8;
+ desired_len = desired_len | (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+
+ saved_mem = buf_space - counter;
+ needed = CEIL((desired_len-unused),8) - no_bytes;
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (3 + no_bytes);
+ if((counter<0) ||
+ (ret=insert_bits_as_bits(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ case 47:
+ /* Same as case 45 apart from By1 and By2, the desired length
+ and the length of Bin, which are in two bytes each. */
+ desired_len = (int)*(++in_ptr);
+ desired_len = desired_len << 8;
+ desired_len = desired_len | (int)*(++in_ptr);
+ no_bytes=(int)*(++in_ptr);
+ no_bytes = no_bytes << 8;
+ no_bytes = no_bytes | (int)*(++in_ptr);
+
+ saved_mem = buf_space - counter;
+ needed = CEIL((desired_len-unused),8) - no_bytes;
+ if (saved_mem < needed) {
+ /* Have to allocate more memory */
+ buf_size += needed;
+ buf_space += needed;
+ if (realloc_memory(drv_binary,buf_size,&ptr,
+ &complete_buf) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+
+ counter -= (4 + no_bytes);
+ if((counter<0) ||
+ (ret=insert_bits_as_bits(desired_len,no_bytes,&in_ptr,
+ &ptr,&unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ buf_space -= ret;
+ break;
+
+ default:
+ return ASN1_ERROR;
+ }
+ in_ptr++;
+ }
+ /* The returned buffer must be at least one byte and
+ it must be octet aligned */
+ if ((unused == 8) && (ptr != complete_buf))
+ return (ptr - complete_buf);
+ else {
+ ptr++; /* octet align buffer */
+ return (ptr - complete_buf);
+ }
+}
+
+
+int realloc_memory(ErlDrvBinary **drv_binary,
+ int amount,
+ unsigned char **ptr,
+ unsigned char **complete_buf) {
+
+ ErlDrvBinary *tmp_bin;
+ int i;
+
+/* printf("realloc_momory: amount = %d\n",amount); */
+ if ((tmp_bin=driver_realloc_binary(*drv_binary,amount)) == NULL) {
+ /*error handling due to memory allocation failure */
+/* printf("error when allocating memory\n"); */
+ return ASN1_ERROR;
+ }else {
+ i = *ptr - *complete_buf;
+ *drv_binary=tmp_bin;
+ *complete_buf = (*drv_binary)->orig_bytes;
+ *ptr = *complete_buf + i;
+ }
+ return ASN1_OK;
+}
+
+
+int insert_most_sign_bits(int no_bits,
+ unsigned char val,
+ unsigned char **output_ptr,
+ int *unused) {
+ unsigned char *ptr = *output_ptr;
+
+ if (no_bits < *unused){
+ *ptr = *ptr | (val >> (8 - *unused));
+ *unused -= no_bits;
+ } else if (no_bits == *unused) {
+ *ptr = *ptr | (val >> (8 - *unused));
+ *unused = 8;
+ *++ptr = 0x00;
+ } else {
+ *ptr = *ptr | (val >> (8 - *unused));
+ *++ptr = 0x00;
+ *ptr = *ptr | (val << *unused);
+ *unused = 8 - (no_bits - *unused);
+ }
+ *output_ptr = ptr;
+ return ASN1_OK;
+}
+
+
+int insert_least_sign_bits(int no_bits,
+ unsigned char val,
+ unsigned char **output_ptr,
+ int *unused) {
+ unsigned char *ptr = *output_ptr;
+ int ret = 0;
+
+ if (no_bits < *unused){
+ *ptr = *ptr | (val << (*unused - no_bits));
+ *unused -= no_bits;
+ } else if (no_bits == *unused){
+ *ptr = *ptr | val;
+ *unused = 8;
+ *++ptr = 0x00;
+ ret++;
+ } else {
+ /* first in the begun byte in the completed buffer insert
+ so many bits that fit, then insert the rest in next byte.*/
+ *ptr = *ptr | (val >> (no_bits - *unused));
+ *++ptr = 0x00;
+ ret++;
+ *ptr = *ptr | (val << (8 - (no_bits - *unused)));
+ *unused = 8 - (no_bits - *unused);
+ }
+ *output_ptr = ptr;
+ return ret;
+}
+
+/* pad_bits adds no_bits bits in the buffer that output_ptr
+ points at.
+ */
+int pad_bits(int no_bits, unsigned char **output_ptr, int *unused)
+ {
+ unsigned char *ptr = *output_ptr;
+ int ret = 0;
+
+ while (no_bits > 0) {
+ if(*unused == 1){
+ *unused = 8;
+ *++ptr = 0x00;
+ ret++;
+ } else
+ (*unused)--;
+ no_bits--;
+ }
+ *output_ptr = ptr;
+ return ret;
+ }
+
+
+/* insert_bits_as_bits removes no_bytes bytes from the buffer that in_ptr
+ points at and takes the desired_no leftmost bits from those removed
+ bytes and inserts them in the buffer(output buffer) that ptr points at.
+ The unused parameter tells how many bits that are not set in the
+ actual byte in the output buffer. If desired_no is more bits than the
+ input buffer has in no_bytes bytes, then zero bits is padded.*/
+int insert_bits_as_bits(int desired_no,
+ int no_bytes,
+ unsigned char **input_ptr,
+ unsigned char **output_ptr,
+ int *unused)
+{
+ unsigned char *in_ptr = *input_ptr;
+ unsigned char val;
+ int no_bits, ret, ret2;
+
+ if (desired_no == (no_bytes * 8)) {
+ if(insert_octets_unaligned(no_bytes,&in_ptr,output_ptr,*unused)
+ == ASN1_ERROR)
+ return ASN1_ERROR;
+ ret = no_bytes;
+ }
+ else if (desired_no < (no_bytes * 8)) {
+/* printf("insert_bits_as_bits 1\n\r"); */
+ if(insert_octets_unaligned(desired_no/8,&in_ptr,output_ptr,*unused)
+ == ASN1_ERROR)
+ return ASN1_ERROR;
+/* printf("insert_bits_as_bits 2\n\r"); */
+ val = *++in_ptr;
+/* printf("val = %d\n\r",(int)val); */
+ no_bits = desired_no % 8;
+/* printf("no_bits = %d\n\r",no_bits); */
+ insert_most_sign_bits(no_bits,val,output_ptr,unused);
+ ret = CEIL(desired_no,8);
+ }
+ else {
+ if(insert_octets_unaligned(no_bytes,&in_ptr,output_ptr,*unused)
+ == ASN1_ERROR)
+ return ASN1_ERROR;
+ ret2 = pad_bits(desired_no - (no_bytes * 8),output_ptr,unused);
+/* printf("ret2 = %d\n\r",ret2); */
+ ret = CEIL(desired_no,8);
+/* printf("ret = %d\n\r",ret); */
+ }
+/* printf("*unused = %d\n\r",*unused); */
+ *input_ptr = in_ptr;
+ return ret;
+}
+
+
+/* insert_octets_as_bits_exact_len */
+int
+insert_octets_as_bits_exact_len(int desired_len,
+ int in_buff_len,
+ unsigned char **in_ptr,
+ unsigned char **ptr,
+ int *unused)
+{
+ int ret = 0;
+ int ret2 = 0;
+
+ if (desired_len == in_buff_len) {
+ if ((ret = insert_octets_as_bits(in_buff_len,in_ptr,ptr,unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+ else if(desired_len > in_buff_len) {
+ if((ret = insert_octets_as_bits(in_buff_len,in_ptr,ptr,unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ /* now pad with zero bits */
+/* printf("~npad_bits: called with %d bits padding~n~n~r",desired_len - in_buff_len); */
+ if ((ret2=pad_bits(desired_len - in_buff_len,ptr,unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+ else {/* desired_len < no_bits */
+ if ((ret=insert_octets_as_bits(desired_len,in_ptr,ptr,unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ /* now remove no_bits - desired_len bytes from in buffer */
+ *in_ptr += (in_buff_len - desired_len);
+ }
+ return (ret+ret2);
+}
+
+
+
+/* insert_octets_as_bits takes no_bytes bytes from the buffer that input_ptr
+ points at and inserts the least significant bit of it in the buffer that
+ output_ptr points at. Each byte in the input buffer must be 1 or 0
+ otherwise the function returns ASN1_ERROR. The output buffer is concatenated
+ without alignment.
+ */
+int insert_octets_as_bits(int no_bytes,
+ unsigned char **input_ptr,
+ unsigned char **output_ptr,
+ int *unused)
+{
+ unsigned char *in_ptr = *input_ptr;
+ unsigned char *ptr = *output_ptr;
+ int used_bits = 8 - *unused;
+
+ while (no_bytes > 0) {
+ switch (*++in_ptr) {
+ case 0:
+ if(*unused == 1){
+ *unused = 8;
+ *++ptr = 0x00;
+ } else
+ (*unused)--;
+ break;
+ case 1:
+ if(*unused == 1){
+ *ptr = *ptr | 1;
+ *unused = 8;
+ *++ptr = 0x00;
+ } else {
+ *ptr = *ptr | (1 << (*unused - 1));
+ (*unused)--;
+ }
+ break;
+ default:
+ return ASN1_ERROR;
+ }
+ no_bytes--;
+ }
+ *input_ptr = in_ptr;
+ *output_ptr = ptr;
+ return ((used_bits+no_bytes) / 8); /*return number of new bytes
+ in completed buffer */
+}
+
+/* insert_octets inserts bytes from the input buffer, *input_ptr,
+ into the output buffer, *output_ptr. Before the first byte is
+ inserted the input buffer is aligned.
+ */
+int insert_octets(int no_bytes,
+ unsigned char **input_ptr,
+ unsigned char **output_ptr,
+ int *unused)
+{
+ unsigned char *in_ptr = *input_ptr;
+ unsigned char *ptr = *output_ptr;
+ int ret = 0;
+
+ if (*unused != 8) {/* must align before octets are added */
+ *++ptr = 0x00;
+ ret++;
+ *unused = 8;
+ }
+ while(no_bytes > 0) {
+ *ptr = *(++in_ptr);
+ *++ptr = 0x00;
+ /* *unused = *unused - 1; */
+ no_bytes--;
+ }
+ *input_ptr = in_ptr;
+ *output_ptr = ptr;
+ return (ret + no_bytes);
+}
+
+/* insert_octets_unaligned inserts bytes from the input buffer, *input_ptr,
+ into the output buffer, *output_ptr.No alignment is done.
+ */
+int insert_octets_unaligned(int no_bytes,
+ unsigned char **input_ptr,
+ unsigned char **output_ptr,
+ int unused)
+{
+ unsigned char *in_ptr = *input_ptr;
+ unsigned char *ptr = *output_ptr;
+ int n = no_bytes;
+ unsigned char val;
+
+ while (n > 0) {
+ if (unused == 8) {
+ *ptr = *++in_ptr;
+ *++ptr = 0x00;
+ }else {
+ val = *++in_ptr;
+ *ptr = *ptr | val >> (8 - unused);
+ *++ptr = 0x00;
+ *ptr = val << unused;
+ }
+ n--;
+ }
+ *input_ptr = in_ptr;
+ *output_ptr = ptr;
+ return no_bytes;
+}
+
+
+int insert_octets_except_unused(int no_bytes,
+ unsigned char **input_ptr,
+ unsigned char **output_ptr,
+ int *unused,
+ int in_unused)
+{
+ unsigned char *in_ptr = *input_ptr;
+ unsigned char *ptr = *output_ptr;
+ int val, no_bits;
+ int ret = 0;
+
+ if (in_unused == 0){
+/* printf("%d: insert_octets_except_unused: if\n\r",__LINE__); */
+ if ((ret = insert_octets_unaligned(no_bytes,&in_ptr,&ptr,
+ *unused)) == ASN1_ERROR)
+ return ASN1_ERROR;
+ }
+ else {
+/* printf("%d: insert_octets_except_unused: else\n\r",__LINE__); */
+ if ((ret=insert_octets_unaligned(no_bytes - 1,&in_ptr,&ptr,*unused)) != ASN1_ERROR) {
+ val = (int) *(++in_ptr);
+ no_bits = 8 - in_unused;
+ /* no_bits is always less than *unused since the buffer is
+ octet aligned after insert:octets call, so the following
+ if clasuse is obsolete I think */
+ if(no_bits < *unused){
+ *ptr = *ptr | (val >> (8 - *unused));
+ *unused = *unused - no_bits;
+ } else if (no_bits == *unused) {
+ *ptr = *ptr | (val >> (8 - *unused));
+ *++ptr = 0x00;
+ ret++;
+ *unused = 8;
+ } else {
+ *ptr = *ptr | (val >> (8 - *unused));
+ *++ptr = 0x00;
+ ret++;
+ *ptr = *ptr | (val << *unused);
+ *unused = 8 - (no_bits - *unused);
+ }
+ } else
+ return ASN1_ERROR;
+ }
+ *input_ptr = in_ptr;
+ *output_ptr = ptr;
+/* printf("%d: insert_octets_except_unused: ret=%d\n\r",__LINE__,ret); */
+ return ret;
+}
+
+
+
+/*
+ *
+ * This section defines functionality for the partial decode of a
+ * BER encoded message
+ *
+ */
+
+/*
+ * int decode(ErlDrvBinary **drv_binary,unsigned char *decode_buf,
+ * unsigned char *in_buf, int in_buf_len)
+ * drv_binary is a pointer to a pointer to an allocated driver binary.
+ * in_buf is a pointer into the buffer of incoming bytes.
+ * in_buf_len is the length of the incoming buffer.
+ * The function reads the bytes in the incoming buffer and structures
+ * it in a nested way as Erlang terms. The buffer contains data in the
+ * order tag - length - value. Tag, length and value has the following
+ * format:
+ * A tag is normally one byte but may be of any length, if the tag number
+ * is greater than 30. +----------+
+ * |CL|C|NNNNN|
+ * +----------+
+ * If NNNNN is 31 then will the 7 l.s.b of each of the following tag number
+ * bytes contain the tag number. Each tag number byte that is not the last one
+ * has the m.s.b. set to 1.
+ * The length can be short definite length (sdl), long definite length (ldl)
+ * or indefinite length (il).
+ * sdl: +---------+ the L bits is the length
+ * |0|LLLLLLL|
+ * +---------+
+ * ldl: +---------+ +---------+ +---------+ +-----------+
+ * |1|lllllll| |first len| | | |the Nth len|
+ * +---------+ +---------+ +---------+ ... +-----------+
+ * The first byte tells how many len octets will follow, max 127
+ * il: +---------+ +----------------------+ +--------+ +--------+
+ * |1|0000000| |content octets (Value)| |00000000| |00000000|
+ * +---------+ +----------------------+ +--------+ +--------+
+ * The value octets are preceded by one octet and followed by two
+ * exactly as above. The value must be some tag-length-value encoding.
+ *
+ * The function returns a value in Erlnag term format:
+ * {{TagNo,Value},Rest}
+ * TagNo is an integer ((CL bsl 16) + tag number) which limits the tag number
+ * to 65535.
+ * Value is a binary if the C bit in tag was unset, otherwise (if tag was
+ * constructed) Value is a list, List.
+ * List is like: [{TagNo,Value},{TagNo,Value},...]
+ * Rest is a binary, i.e. the undecoded part of the buffer. Most often Rest
+ * is the empty binary.
+ * If some error occured during the decoding of the in_buf an error is returned.
+ */
+int decode_begin(ErlDrvBinary **drv_binary,unsigned char *in_buf, int in_buf_len, unsigned int *err_pos)
+{
+ int maybe_ret;
+ char *decode_buf = (*drv_binary)->orig_bytes;
+ int ei_index = 0;
+ int ib_index = 0;
+ /* ei_index is the index used by the ei functions to encode an
+ Erlang term into the buffer decode_buf */
+ /* ib_index is the index were to read the next byte from in_buf */
+
+
+#ifdef ASN1_DEBUG
+ printf("decode_begin1: ei_index=%d, ib_index=%d\n\r",ei_index,ib_index);
+#endif
+ /* the first byte must be a "version magic" */
+ if(ei_encode_version(decode_buf,&ei_index) == ASN1_ERROR)
+ return ASN1_ERROR; /* 1 byte */
+#ifdef ASN1_DEBUG
+ printf("decode_begin2: ei_index=%d, ib_index=%d\n\r",ei_index,ib_index);
+#endif
+ if (ei_encode_tuple_header(decode_buf,&ei_index,2) == ASN1_ERROR)
+ return ASN1_ERROR; /* 2 bytes */
+#ifdef ASN1_DEBUG
+ printf("decode_begin3: ei_index=%d, ib_index=%d\n\r",ei_index,ib_index);
+#endif
+ if((maybe_ret=decode(drv_binary,&ei_index,in_buf,&ib_index,in_buf_len)) <= ASN1_ERROR)
+ {
+ *err_pos = ib_index;
+#ifdef ASN1_DEBUG
+ printf("err_pos=%d,ib_index=%d\r\n",*err_pos,ib_index);
+#endif
+ return maybe_ret;
+ };
+
+ decode_buf = (*drv_binary)->orig_bytes; /* maybe a realloc during decode_value */
+#ifdef ASN1_DEBUG
+ printf("decode_begin4: in_buf_len=%d, ei_index=%d, ib_index=%d\n\r",
+ in_buf_len,ei_index,ib_index);
+#endif
+ /* "{{TagNo,Value},Rest}" */
+ if (ei_encode_binary(decode_buf,&ei_index,&(in_buf[ib_index]),in_buf_len-ib_index)
+ == ASN1_ERROR) /* at least 5 bytes */
+ return ASN1_ERROR;
+#ifdef ASN1_DEBUG
+ printf("decode_begin5: ei_index=%d, ib_index=%d\n\r",ei_index,ib_index);
+#endif
+ return ei_index;
+}
+
+int decode(ErlDrvBinary **drv_binary,int *ei_index,unsigned char *in_buf,
+ int *ib_index, int in_buf_len)
+{
+ int maybe_ret;
+ char *decode_buf = (*drv_binary)->orig_bytes;
+ int form;
+#ifdef ASN1_DEBUG
+ printf("decode 1\n\r");
+#endif
+ if (((*drv_binary)->orig_size - *ei_index) < 19) {/* minimum amount of bytes */
+ /* allocate more memory */
+ if (realloc_decode_buf(drv_binary,(*drv_binary)->orig_size * 2) ==
+ ASN1_ERROR)
+ return ASN1_ERROR;
+ decode_buf = (*drv_binary)->orig_bytes;
+ }
+/* printf("decode 2\n\r"); */
+ /* "{" */
+ if (ei_encode_tuple_header(decode_buf,ei_index,2) == ASN1_ERROR)
+ return ASN1_ERROR; /* 2 bytes */
+#ifdef ASN1_DEBUG
+ printf("decode 3:orig_size=%d, ei_index=%d, ib_index=%d\n\r",(*drv_binary)->orig_size,*ei_index,*ib_index);
+#endif
+
+ /*buffer must hold at least two bytes*/
+ if ((*ib_index +2) > in_buf_len)
+ return ASN1_VALUE_ERROR;
+ /* "{{TagNo," */
+ if ((form = decode_tag(decode_buf,ei_index,in_buf,in_buf_len,ib_index)) <= ASN1_ERROR)
+ return form; /* 5 bytes */
+#ifdef ASN1_DEBUG
+ printf("i_i=%d,in_buf_len=%d\r\n",*ei_index,in_buf_len);
+#endif
+ if (*ib_index >= in_buf_len){
+ return ASN1_TAG_ERROR;
+ }
+#ifdef ASN1_DEBUG
+ printf("decode 5 ib_index=%d\n\r",*ib_index);
+#endif
+ /* buffer must hold at least one byte (0 as length and nothing as
+ value) */
+ /* "{{TagNo,Value}," */
+ if ((maybe_ret=decode_value(ei_index,in_buf,ib_index,drv_binary,form,
+ in_buf_len)) <= ASN1_ERROR)
+ return maybe_ret; /* at least 5 bytes */
+#ifdef ASN1_DEBUG
+ printf("decode 7\n\r");
+#endif
+ return *ei_index;
+}
+
+/*
+ * decode_tag decodes the BER encoded tag in in_buf and puts it in the
+ * decode_buf encoded by the Erlang extern format as an Erlang term.
+ */
+int decode_tag(char *decode_buf,int *db_index,unsigned char *in_buf,
+ int in_buf_len, int *ib_index)
+{
+ int tag_no, tmp_tag, form;
+
+
+ /* first get the class of tag and bit shift left 16*/
+ tag_no = ((MASK(in_buf[*ib_index],ASN1_CLASS)) << 10);
+
+ form = (MASK(in_buf[*ib_index],ASN1_FORM));
+#ifdef ASN1_DEBUG
+ printf("decode_tag0:ii=%d, tag_no=%d, form=%d.\r\n",
+ *ib_index,tag_no,form);
+#endif
+
+ /* then get the tag number */
+ if((tmp_tag = (int) INVMASK(in_buf[*ib_index],ASN1_CLASSFORM)) < 31) {
+ ei_encode_ulong(decode_buf,db_index,tag_no+tmp_tag); /* usual case */
+ (*ib_index)++;
+#ifdef ASN1_DEBUG
+ printf("decode_tag1:ii=%d.\r\n",*ib_index);
+#endif
+ }
+ else
+ {
+ int n = 0; /* n is used to check that the 64K limit is not
+ exceeded*/
+#ifdef ASN1_DEBUG
+ printf("decode_tag1:ii=%d, in_buf_len=%d.\r\n",*ib_index,in_buf_len);
+#endif
+
+ /* should check that at least three bytes are left in
+ in-buffer,at least two tag byte and at least one length byte */
+ if ((*ib_index +3) > in_buf_len)
+ return ASN1_VALUE_ERROR;
+ (*ib_index)++;
+#ifdef ASN1_DEBUG
+ printf("decode_tag2:ii=%d.\r\n",*ib_index);
+#endif
+ /* The tag is in the following bytes in in_buf as
+ 1ttttttt 1ttttttt ... 0ttttttt, where the t-bits
+ is the tag number*/
+ /* In practice is the tag size limited to 64K, i.e. 16 bits. If
+ the tag is greater then 64K return an error */
+ while (((tmp_tag = (int)in_buf[*ib_index]) >= 128) && n < 2){
+ /* m.s.b. = 1 */
+ tag_no = tag_no + (MASK(tmp_tag,ASN1_LONG_TAG) << 7);
+ (*ib_index)++;
+#ifdef ASN1_DEBUG
+ printf("decode_tag3:ii=%d.\r\n",*ib_index);
+#endif
+ n++;
+ };
+ if ((n==2) && in_buf[*ib_index] > 3)
+ return ASN1_TAG_ERROR; /* tag number > 64K */
+ tag_no = tag_no + in_buf[*ib_index];
+ (*ib_index)++;
+#ifdef ASN1_DEBUG
+ printf("decode_tag4:ii=%d.\r\n",*ib_index);
+#endif
+ ei_encode_ulong(decode_buf,db_index,tag_no);
+ }
+ return form;
+}
+
+
+/*
+ * decode_value decodes the BER encoded length and value fields in the
+ * in_buf and puts the value part in the decode_buf as an Erlang term
+ * encoded by the Erlang extern format
+ */
+int decode_value(int *ei_index,unsigned char *in_buf,
+ int *ib_index,ErlDrvBinary **drv_binary,int form,
+ int in_buf_len)
+{
+ int maybe_ret;
+ char *decode_buf = (*drv_binary)->orig_bytes;
+ int len, lenoflen;
+ int indef = 0;
+
+#ifdef ASN1_DEBUG
+ printf("decode_value1:ib_index=%d\n\r",*ib_index);
+#endif
+ if (((in_buf[*ib_index]) & 0x80) == ASN1_SHORT_DEFINITE_LENGTH) {
+ len = in_buf[*ib_index];
+ if (len > (in_buf_len - (*ib_index + 1)))
+ return ASN1_LEN_ERROR;
+ }
+ else if (in_buf[*ib_index] == ASN1_INDEFINITE_LENGTH)
+ indef = 1;
+ else /* long definite length */ {
+ lenoflen = (in_buf[*ib_index] & 0x7f); /*length of length */
+#ifdef ASN1_DEBUG
+ printf("decode_value,lenoflen:%d\r\n",lenoflen);
+#endif
+ len = 0;
+ while (lenoflen-- && (*ib_index <= in_buf_len)) {
+ (*ib_index)++;
+#ifdef ASN1_DEBUG
+ printf("decode_value1:ii=%d.\r\n",*ib_index);
+#endif
+ len = (len << 8) + in_buf[*ib_index];
+ }
+ if (len > (in_buf_len - (*ib_index + 1)))
+ return ASN1_LEN_ERROR;
+ }
+ (*ib_index)++;
+#ifdef ASN1_DEBUG
+ printf("decode_value2:ii=%d.\r\n",*ib_index);
+#endif
+ if (indef == 1)
+ { /* in this case it is desireably to check that indefinite length
+ end bytes exist in inbuffer */
+ while (!(in_buf[*ib_index]==0 && in_buf[*ib_index + 1]==0)) {
+#ifdef ASN1_DEBUG
+ printf("decode_value while:ib_index=%d in_buf_len=%d\n\r",
+ *ib_index,in_buf_len);
+#endif
+ if(*ib_index >= in_buf_len)
+ return ASN1_INDEF_LEN_ERROR;
+ ei_encode_list_header(decode_buf,ei_index,1); /* 5 bytes */
+ if((maybe_ret=decode(drv_binary,ei_index,in_buf,
+ ib_index,in_buf_len)) <= ASN1_ERROR)
+ return maybe_ret;
+ decode_buf = (*drv_binary)->orig_bytes;
+ }
+ (*ib_index) += 2; /* skip the indefinite length end bytes */
+#ifdef ASN1_DEBUG
+ printf("decode_value3:ii=%d.\r\n",*ib_index);
+#endif
+ ei_encode_empty_list(decode_buf,ei_index); /* 1 byte */
+ }
+ else if (form == ASN1_CONSTRUCTED)
+ {
+ int end_index = *ib_index + len;
+ if(end_index > in_buf_len)
+ return ASN1_LEN_ERROR;
+ while (*ib_index < end_index) {
+
+#ifdef ASN1_DEBUG
+ printf("decode_value3:*ib_index=%d, end_index=%d\n\r",*ib_index,end_index);
+#endif
+ ei_encode_list_header(decode_buf,ei_index,1); /* 5 bytes */
+ if((maybe_ret=decode(drv_binary,ei_index,in_buf,
+ ib_index,in_buf_len))<=ASN1_ERROR)
+ return maybe_ret;
+ decode_buf = (*drv_binary)->orig_bytes;
+ }
+ ei_encode_empty_list(decode_buf,ei_index); /* 1 byte */
+ }
+ else
+ {
+ if (((*drv_binary)->orig_size - *ei_index) < 10+len) { /* 5+len for the binary*/
+ if (realloc_decode_buf(drv_binary,(*drv_binary)->orig_size * 2) ==
+ ASN1_ERROR)
+ return ASN1_ERROR;
+ decode_buf = (*drv_binary)->orig_bytes;
+ }
+ if((*ib_index + len) > in_buf_len)
+ return ASN1_LEN_ERROR;
+ ei_encode_binary(decode_buf,ei_index,&in_buf[*ib_index],len);
+ *ib_index = *ib_index + len;
+#ifdef ASN1_DEBUG
+ printf("decode_value4:ii=%d.\r\n",*ib_index);
+#endif
+ }
+ return ASN1_OK;
+}
+
+int realloc_decode_buf(ErlDrvBinary **drv_binary,int amount) {
+ ErlDrvBinary *tmp_bin;
+
+ if ((tmp_bin=driver_realloc_binary(*drv_binary,amount)) == NULL)
+ return ASN1_ERROR;
+ *drv_binary = tmp_bin;
+ return ASN1_OK;
+}
+
+
+
+/*
+ * int decode_partial(drv_binary,in_buf,in_buf_len)
+ */
+/*
+ * The in_buf contains two parts: first information about which value
+ * will be decoded, as a sequence of tags and tag codes, then the
+ * encoded BER value. First of all comes a length field that tells how
+ * many following bytes contains the sequence of tags. Then starts the
+ * BER encoded message. The tag sequence length field is a single
+ * byte. The sequence of tags/tag codes may be one of the codes
+ * ASN1_SKIPPED, ASN1_CHOOSEN and a tag or ASN1_OPTIONAL and a
+ * tag. ASN1_SKIPPED means that the following tag is mandatory and is
+ * skipped. ASN1_CHOOSEN means that the value of this tag shall, if
+ * this was the last tag in tag sequence, be returned or be searched
+ * in for the next tag. ASN1_OPTIONAL means that this tag shall be
+ * skipped but it may be missing. Each tag in the tag sequence
+ * correspond to a tag in the BER encoded message. If the decode
+ * arives to a position where there is no matching tag, an error is
+ * returned (if it wasn't the last tag and it was OPTIONAL). After the
+ * right value has been detected it is returned in the out_buf.
+ *
+ */
+int decode_partial(ErlDrvBinary **drv_binary,unsigned char *in_buf, int in_buf_len)
+{
+ char *out_buf = (*drv_binary)->orig_bytes;
+ int tag_index_val = 1;
+ int msg_index_val;
+ int *msg_index, *tag_index, tmp_index;
+ int tag_seq_length;
+ char tag_code; /* one of ASN1_SKIPPED, ASN1_OPTIONAL, ASN1_CHOOSEN */
+ int wanted_tag, next_tag;
+ int buf_end_index = in_buf_len;
+ int ret = 0, length, old_index;
+
+ tag_index = &tag_index_val;
+ tag_seq_length = in_buf[0];
+ msg_index = &msg_index_val;
+ *msg_index = tag_seq_length + 1;
+
+
+/* printf("decode_partial 1: in_buf_len=%d, tag_index=%d, msg_index=%d\r\n,tag_seq_length=%d\r\n",in_buf_len,*tag_index,*msg_index,tag_seq_length); */
+ while(*tag_index < tag_seq_length) {
+ switch(in_buf[*tag_index]) {
+ case ASN1_SKIPPED:
+/* printf("decode_partial ASN1_SKIPPED: in_buf[*msg_index]=%d\r\n",in_buf[*msg_index]); */
+ (*tag_index)++;
+/* printf("decode_partial ASN1_SKIPPED 2: *msg_index=%d\r\n",*msg_index); */
+ skip_tag(in_buf,msg_index,buf_end_index);
+/* printf("decode_partial ASN1_SKIPPED 3: *msg_index=%d\r\n",*msg_index); */
+ skip_length_and_value(in_buf,msg_index,buf_end_index);
+/* printf("decode_partial ASN1_SKIPPED 4: *msg_index=%d\r\n",*msg_index); */
+ break;
+ case ASN1_OPTIONAL:
+ (*tag_index)++;
+/* printf("decode_partial ASN1_OPTIONAL: in_buf[*tag_index]=%d\r\n",in_buf[*tag_index]); */
+ wanted_tag = in_buf[*tag_index];
+ (*tag_index)++;
+ tmp_index = *msg_index;
+ next_tag = get_tag(in_buf,msg_index,buf_end_index);
+ if (wanted_tag != next_tag) {
+ *msg_index = tmp_index;
+ } else
+ skip_length_and_value(in_buf,msg_index,buf_end_index);
+ break;
+ case ASN1_CHOOSEN:
+/* printf("decode_partial ASN1_CHOOSEN: in_buf[*msg_index]=%d, *msg_index=%d\r\n",in_buf[*msg_index],*msg_index); */
+ (*tag_index)++;
+ wanted_tag = in_buf[*tag_index];
+ (*tag_index)++;
+ old_index = *msg_index;
+/* printf("decode_partial ASN1_CHOOSEN 2: *msg_index=%d\r\n",*msg_index); */
+ next_tag = get_tag(in_buf,msg_index,buf_end_index);
+/* printf("decode_partial ASN1_CHOOSEN 3: *msg_index=%d\r\n,wanted_tag=%d, next_tag=%d\r\n",*msg_index,wanted_tag,next_tag); */
+ if (wanted_tag != next_tag)
+ return ASN1_NOVALUE; /* an empty binary will be returned to Erlang */
+ if (*tag_index == (tag_seq_length + 1)) {
+ /* get the value and return*/
+ if((ret = get_value(out_buf,in_buf,msg_index,buf_end_index)) <= ASN1_ERROR)
+ return ASN1_ERROR;
+ return ret;
+ }
+ else {
+ /* calculate the length of the sub buffer and let *msg_index
+ be at the value part of this BER encoded type*/
+ int indef;
+ indef = 0;
+ length = get_length(in_buf,msg_index,&indef,buf_end_index);
+/* printf("decode_partial ASN1_CHOOSEN 4: length=%d, *msg_index=%d\r\n",length,*msg_index); */
+ if ((length == 0) && (indef == 1)) {
+ /* indefinite length of value */
+ old_index = *msg_index;
+ length = skip_length_and_value(in_buf,msg_index,buf_end_index);
+ *msg_index = old_index;
+ buf_end_index = *msg_index + length - 2;
+ /* remove two bytes due to indefinete length end zeros */
+ } else
+ buf_end_index = (*msg_index + length);
+ }
+ break;
+ default:
+ return ASN1_ERROR;
+ }
+ }
+ return ASN1_ERROR;
+}
+
+
+/*
+ * int skip_tag(unsigned char *in_buf,int *index,int buf_len)
+ * steps past the BER encoded tag in in_buf and updates *index.
+ * Returns the number of skipped bytes.
+ */
+int skip_tag(unsigned char *in_buf,int *index,int buf_len)
+{
+ int start_index = *index;
+ if ((MASK(in_buf[*index],ASN1_TAG)) == 31){
+ do {
+ (*index)++;
+ if (*index >= buf_len)
+ return ASN1_ERROR;
+ }
+ while(in_buf[*index] >=128);
+ }
+ (*index)++;
+ return (*index - start_index);
+}
+
+
+/*
+ * int skip_length_and_value(unsigned char *in_buf,int *index,int buf_len)
+ * steps past the BER encoded length and value in in_buf and updates *index.
+ * returns the length if the skipped "length value".
+ * Returns the number of skipped bytes.
+ */
+int skip_length_and_value(unsigned char *in_buf,int *index,int buf_len)
+{
+ long len;
+ int indef = 0, lenoflen;
+ int start_index = *index;
+
+ if ((MASK(in_buf[*index],0x80)) == ASN1_SHORT_DEFINITE_LENGTH){
+ len = in_buf[*index];
+ if (len > (buf_len - (*index + 1)))
+ return ASN1_LEN_ERROR;
+ } else if (in_buf[*index] == ASN1_INDEFINITE_LENGTH)
+ indef = 1;
+ else /* long definite length */ {
+ lenoflen = (in_buf[*index] & 0x7f); /*length of length */
+ len = 0;
+ while (lenoflen--) {
+ (*index)++;
+ len = (len << 8) + in_buf[*index];
+ }
+ if (len > (buf_len - (*index + 1)))
+ return ASN1_LEN_ERROR;
+ }
+ (*index)++;
+ if (indef == 1)
+ {
+ while(!(in_buf[*index]==0 && in_buf[*index + 1]==0)) {
+ skip_tag(in_buf,index,buf_len);
+ skip_length_and_value(in_buf,index,buf_len);
+ }
+ (*index) += 2;
+ }
+ else
+ (*index) += len;
+ return (*index - start_index);
+}
+
+/* int get_tag(unsigned char *in_buf,int *index)
+ *
+ * assumes next byte/bytes in in_buf is an encoded BER tag. A tag
+ * number has theoretically no upper limit in size. Here the tag
+ * number is assumed to be less than 64K. Returns an integer value
+ * on the format:
+ * xxxxxxxx xxxxxxcc tttttttt tttttttt
+ * the x-bits are 0 (insignificant)
+ * the c-bits are the class of the tag
+ * the t-bits are the tag number. This implies that the tag number
+ * is limited to 64K-1
+ *
+ */
+int get_tag(unsigned char *in_buf,int *index,int buf_len)
+{
+ int tag_no = 0,tmp_tag = 0;
+
+ tag_no = (MASK(in_buf[*index],ASN1_CLASSFORM));
+ if ((MASK(in_buf[*index],ASN1_TAG)) == ASN1_TAG) {
+ /* long form of tag */
+ do {
+ (*index)++;
+ if (*index >= buf_len)
+ return ASN1_TAG_ERROR;
+ tmp_tag = tmp_tag << 7;
+ tmp_tag += (MASK(in_buf[*index],ASN1_LONG_TAG));
+ } while (in_buf[*index] >= 128);
+ (*index)++;
+ tag_no = tag_no + tmp_tag;
+ } else {
+ tag_no += (MASK(in_buf[*index],ASN1_TAG));
+ (*index)++;
+ }
+ if (*index >= buf_len)
+ return ASN1_TAG_ERROR;
+ return tag_no;
+}
+
+
+/*
+ * int get_value(char *out_buf,unsigned char *in_buf,
+ * int *msg_index,int in_buf_len)
+ */
+/* assumes next byte/bytes in in_buf is an encoded BER value preceeded by a BER encoded length. Puts value in out_buf.
+ */
+int get_value(char *out_buf,
+ unsigned char *in_buf,
+ int *msg_index,
+ int in_buf_len)
+{
+ int len, lenoflen, indef=0, skip_len;
+ int ret=0;
+ int start_index, out_index = 0;
+
+/* printf("get_value 1\n\r"); */
+ if (in_buf[*msg_index] < 0x80){ /* short definite length */
+ len = in_buf[*msg_index];
+/* printf("short definite length\r\n"); */
+ } else if (in_buf[*msg_index] > 0x80) { /* long definite length */
+ lenoflen = (in_buf[*msg_index] & 0x7f); /*length of length */
+ len = 0;
+ while (lenoflen--) {
+ (*msg_index)++;
+ len = (len << 8) + in_buf[*msg_index];
+ }
+ if (len > (in_buf_len - (*msg_index + 1)))
+ return ASN1_LEN_ERROR;
+ } else
+ indef = 1;
+ (*msg_index)++;
+/* printf("get_value 2: len = %d, *msg_index = %d\r\n",len,*msg_index); */
+ if (indef == 1) {
+ while(!(in_buf[*msg_index]==0 && in_buf[*msg_index + 1]==0)) {
+ start_index = *msg_index;
+ skip_len = skip_tag(in_buf,msg_index,in_buf_len);
+/* printf("get_value 3: skip_len=%d,start_index=%d,*msg_index=%d\n\r", */
+/* skip_len,start_index,*msg_index); */
+ memcpy(&out_buf[ret],&in_buf[start_index],skip_len);
+ ret += skip_len;
+ start_index = *msg_index;
+ skip_len = skip_length_and_value(in_buf,msg_index,in_buf_len);
+/* printf("get_value 4: skip_len=%d,start_index=%d,*msg_index=%d\n\r", */
+/* skip_len,start_index,*msg_index); */
+ memcpy(&out_buf[ret],&in_buf[start_index],skip_len);
+ ret += skip_len;
+ }
+ return ret;
+ }
+ else
+ memcpy(&out_buf[ret],&in_buf[*msg_index],len);
+ return len;
+}
+
+
+/*
+ * int get_length(unsigned char *in_buf,int *msg_index)
+ * assumes next byte/bytes contain a BER encoded length field,
+ * which is decoded. The value of the length is returned. If it
+ * is an indefinite length the *indef is set to one.
+ */
+int get_length(unsigned char *in_buf,int *msg_index,
+ int *indef,int in_buf_len)
+{
+ int len=0, lenoflen;
+
+ if (in_buf[*msg_index] < 0x80) /* short definite length */
+ len = in_buf[*msg_index];
+ else if (in_buf[*msg_index] > 0x80) { /* long definite length */
+ lenoflen = (in_buf[*msg_index] & 0x7f); /*length of length */
+ len = 0;
+ while (lenoflen--) {
+ (*msg_index)++;
+ len = (len << 8) + in_buf[*msg_index];
+ }
+ if (len > (in_buf_len - (*msg_index + 1)))
+ return ASN1_LEN_ERROR;
+ } else
+ *indef = 1;
+ (*msg_index)++;
+ return len;
+}