#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "ber.h"
#include "compat.h"
/* berOid - the encoding of an object identifier.
* the encoding of this is a real hassel. First of all you have the same
* anoying problem with length as you do with the strings and sequence. Then
* you have to take the first two oids multiply the first one by 40 and add the
* second one. Then you can go through the rest of the oids and encode them
* however they are not encoded as simple integers or anything they are big
* endian two's compliment stuffed into as few bytes as possible. The way that
* you stuff them into as few bytes as possible and deal with the fact that
* that you don't know how many bytes each suboid is going to be is to use
* the MSB to indicate if there are more bits to follow. Thus you can only
* use the 7 LSB to represent the data. So a normal long can end up being
* up to 5 bytes long.
*/
BerOid::BerOid(unsigned char *str): BerBase(str){
assert(str[0]==OID_TAG);
unsigned char headerlen;
unpack_len(str,headerlen);
assert(eoid=new unsigned char[oidbytes=edatacache->Length()-headerlen]);
memcpy(eoid,str+headerlen,oidbytes);
}
BerEncodedData *BerOid::encode(){
unsigned char headerlen;
unsigned char *retval=start_data(OID_TAG,oidbytes,headerlen);
memcpy(retval+headerlen,eoid,oidbytes);
return new BerEncodedData(retval,headerlen+oidbytes);
}
int unpack_suboid(unsigned char* buf, unsigned int &len){
for(len=0;len<sizeof(long)+1;len++)
if(buf[len]<128)
break;
long val=0;
switch(len){
case 4:
val|=(buf[0]&0x7f)<<28;
buf++;
case 3:
val|=(buf[0]&0x7f)<<21;
buf++;
case 2:
val|=(buf[0]&0x7f)<<14;
buf++;
case 1:
val|=(buf[0]&0x7f)<<7;
buf++;
default:
val|=buf[0];
}
len++;
return val;
}
int pack_suboid(long suboid, unsigned char *outbuf){
unsigned char buf[sizeof(long)+1];
unsigned char len=0;
// spread out the bits
signed char i;
for(i=sizeof(long);i>=0;i--)
buf[i]=(suboid>>7*i)&0x7f;
// mark the bits
for(i=sizeof(long)-1;i>=0;i--)
if(buf[i]>0) {
for(char j=i;j>0;j--)
buf[j]|=0x80;
len=i+1;
break;
}
if(len==0)
len=1;
unsigned char j=0;
for(i=len-1;i>=0;i--)
outbuf[j++]=buf[i];
return len;
}
BerOid::BerOid(CONST char *oidstr,unsigned int len){
assert(oidstr!=NULL);
// should be big enough. I can't think of a case where an oid would expand
// when encoded.
assert(eoid=new unsigned char[len]);
const char *end=oidstr+len;
char *curstr;
long val=strtol(oidstr,&curstr,10);
assert(val!=LONG_MAX && errno!=ERANGE);
if(*curstr==0){
assert(curstr==end);
// only one suboid
oidbytes=pack_suboid(val*40,eoid)+2;
assert(oidbytes<=len);
eoid=(unsigned char*)realloc(eoid,oidbytes);
return;
}
assert(*curstr=='.');
oidstr=curstr+1;
long val2=strtol(oidstr,&curstr,10);
assert(val2!=LONG_MAX && errno!=ERANGE);
oidbytes=pack_suboid(val*40+val2,eoid);
assert(oidbytes<=len);
if(*curstr==0){
assert(curstr==end);
// only two suboids
eoid=(unsigned char*)realloc(eoid,oidbytes);
return;
}
assert(*curstr=='.');
oidstr=curstr+1;
while(oidstr<end){
val=strtol(oidstr,&curstr,10);
assert(val!=LONG_MAX && errno!=ERANGE);
assert(!(*curstr==0 && curstr!=end)); // string too short
assert(!(*curstr!='.' && curstr!=end));
oidstr=curstr+1;
oidbytes+=pack_suboid(val,eoid+oidbytes);
assert(oidbytes<=len);
}
eoid=(unsigned char*)realloc(eoid,oidbytes);
}
int BerOid::print(char *buf, unsigned int len){
unsigned char *cur=eoid;
unsigned int elen=0,totlen=0;
int slen;
int val=unpack_suboid(cur,elen);
cur+=elen;
slen=snprintf(buf,len,"%d.%d",val/40,val%40);
if(slen==-1) return -1;
buf+=slen;
totlen=slen;
len-=slen;
while(cur<eoid+oidbytes){
val=unpack_suboid(cur,elen);
cur+=elen;
slen=snprintf(buf,len,".%d",val);
if(slen==-1) return -1;
buf+=slen;
len-=slen;
totlen+=slen;
}
return totlen;
}
int BerOid::operator==(BerOid &other){
unsigned int len=oidbytes>other.oidbytes?other.oidbytes:oidbytes;
return !memcmp(eoid,other.eoid,len);
}
syntax highlighted by Code2HTML, v. 0.9.1