#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "ber.h"
#include "compat.h"
BerEncodedData::BerEncodedData(unsigned char *dat, unsigned long len):
data(dat),length(len),next(NULL){
assert(data);
}
unsigned char *start_data(Tags type,unsigned int len, unsigned char &headlen){
unsigned char *retstr;
if(len<128){
assert(retstr=new unsigned char[len+2]);
retstr[1]=len;
headlen=2;
} else {
unsigned char buf[sizeof(unsigned long)];
unsigned long nlen=htonl(len);
memcpy(buf,&nlen,sizeof(unsigned long));
//counts the number of leading 0's
for(headlen=0;buf[headlen]==0 && headlen<3;headlen++);
//magic function to to turn this into header length
headlen=sizeof(unsigned long)-headlen+2;
assert(retstr=new unsigned char[len+headlen]);
retstr[1]=0x80-2+headlen;
char k=2;
for(char j=sizeof(unsigned long)+2-headlen;j<=3;j++)
retstr[k++]=buf[j];
}
retstr[0]=type;
return retstr;
}
unsigned long unpack_len(unsigned char *start,unsigned char &headerlen){
if(start[1]<128){
headerlen=2;
return start[1];
}
long sizeofsize=start[1]&0x7f;
assert(sizeofsize<=sizeof(long));
long size=0;
memcpy(((unsigned char*)(&size))+sizeof(long)-sizeofsize,start+2,sizeofsize);
headerlen=2+sizeofsize;
return htonl(size);
}
/* ber class is the basic encoding rules for ASN.1 class. All the classes that
* have encoding are derived from this class.
* len - is the length of the encoded sequence.
* mbufcache - is the the head of the mbuf chain that this class will hand out
* when getmbuf is called. It is set to NULL initially and getmbuf fills
* it in and subsequent calls just return it.
* getmbuf - most classes just use the default getmbuf but sequences need to
* do some special processing and so it must be used be virtual.
* encode - does whatever is necessary to come up with a string to pass out
* the calling function. The string is probably not NULL terminated and
* probably has NULLs in it.
* Len - is used to find out the length of the encoded sequence. The value is
* undefined before an encode is called.
*/
BerEncodedData *BerBase::encode(){
assert(0);
return NULL;
}
BerEncodedData *BerBase::getdata(){
assert(edatacache || (edatacache=encode()));
return edatacache;
}
unsigned long BerBase::fulllen(){
BerEncodedData *bed=getdata();
unsigned long retval=bed->Length();
while(bed->next){
bed=bed->next;
retval+=bed->Length();
}
return retval;
}
BerBase::BerBase(unsigned char* str):next(NULL){
unsigned char *retstr;
unsigned char headerlen;
unsigned long size=unpack_len(str,headerlen);
unsigned long fullen=size+headerlen;
assert(retstr=new unsigned char[fullen]);
memcpy(retstr,str,fullen);
assert(edatacache=new BerEncodedData(retstr,fullen));
}
/*
*
* berNull - is the string 0x05 0x00 there is really not much too it.
*
*/
int BerNull::print(char *buf, unsigned int len){
if(len<4) return -1;
strncpy(buf,"NULL",4);
return 4;
}
BerNull::BerNull(unsigned char *str): BerBase(str){
assert(str[0]==NULL_TAG);
assert(str[1]==0);
}
BerEncodedData *BerNull::encode(){
unsigned char headlen;
return new BerEncodedData(start_data(NULL_TAG,0,headlen),headlen);
}
/* berInt is the encoding of an int in ASN.1
* The encoding is a 0x02 followed by the size of the data then the bytes of
* of the data. The data is stored in the big endian 2's complement using the
* fewest bytes possible. However a 0 still uses one byte. i.e. 0x20 0x01 0x00
*/
int BerInt::print(char *buf, unsigned int len){
return snprintf(buf,len,"%d",val);
}
BerInt::BerInt(unsigned char *str): BerBase(str),val(0){
/* this might cause some problems with the counters that exceede 31 bits in
length but for now it seems ok */
assert(str[0]==INT_TAG||str[0]==COUNTER_TAG);
assert(str[1]<=sizeof(long));
memcpy(((unsigned char*)&val)+sizeof(long)-str[1],str+2,str[1]);
//extend out the sign if necessary
if(str[0]==INT_TAG && str[1]!=sizeof(long) && str[2]&0x80)
memset(&val,0xff,sizeof(long)-str[1]);
// being lazy again some high bit counters may cause a problem.
assert(!(str[0]==COUNTER_TAG && str[1]==sizeof(long) && str[2]&0x80));
val=ntohl(val);
}
BerEncodedData *BerInt::encode(){
unsigned char buf[sizeof(long)];
unsigned char *retval;
unsigned char i,headerlen;
long value=htonl(val);
memcpy(buf,&value,sizeof(long));
// printf("0=%d 1=%d 2=%d 3=%d ",(int)buf[0],(int)buf[1],(int)buf[2],
// (int)buf[3]);
//count the number of bytes that are actually used
for(i=sizeof(long);!buf[sizeof(long)-i] && i>1;i--);
/* zero pad the case when the most significant byte has a 1 in the most
significant bit -- otherwise the other end will interpret it as a negative
number */
if(i!=sizeof(long) && buf[sizeof(long)-i]&0x7f)
i++;
// printf("val=%ld len=%u buf[0]=%u buf[1]=%u buf[2]=%u buf[3]=%u\n",val,
// (unsigned) i,(unsigned) buf[0],(unsigned) buf[1],(unsigned) buf[2],
// (unsigned) buf[3]);
retval=start_data(INT_TAG,i,headerlen);
memcpy(retval+headerlen,buf+sizeof(long)-i,i);
// printf("i=%d ",(int) i);
// printf("va// lue=%d ",val);
// for(int j=0;j<i;j++){
// printf("i=%d val=%d ",j,(int) *(retval+headerlen+j));
// }
// printf("\n");
return new BerEncodedData(retval,headerlen+i);
}
/*
* Number of timeticks since some epoch. In 1/100 seconds
*/
BerTimeTick::BerTimeTick(unsigned char *str): BerBase(str),val(0){
assert(str[0]==TIME_TICK_TAG);
assert(str[1]<=sizeof(unsigned long));
memcpy(((unsigned char*)&val)+sizeof(unsigned long)-str[1],str+2,str[1]);
val=ntohl(val);
}
BerEncodedData *BerTimeTick::encode(){
unsigned char buf[sizeof(unsigned long)];
unsigned char *retval;
unsigned char i,headerlen;
long value=htonl(val);
memcpy(buf,&value,sizeof(unsigned long));
//count the number of bytes that are actually used
for(i=sizeof(long);!buf[sizeof(unsigned long)-i] && i>1;i--);
retval=start_data(TIME_TICK_TAG,i,headerlen);
memcpy(retval+headerlen,buf,i);
return new BerEncodedData(retval,headerlen+i);
}
int BerTimeTick::print(char *buf, unsigned int len){
// hopefully this works by just assming the bits are right rather than
// coercing the value into a different format.
unsigned t1=val%8640000;
unsigned t2=t1%360000;
unsigned t3=t2%6000;
return snprintf(buf,len,"Time: %ud %uh %um %u.%usec (%u)",val/8640000,
t1/360000,t2/6000,t3/100,t3%100,val);
}
/*
* berString is used to encode a string. It is simply the length and the data.
* the only complication is the encoding of the length.
* Also terminates string.
*/
int BerString::copy(char *buf, unsigned int len){
assert(len>stringlen+1);
memcpy(buf,str,stringlen);
buf[stringlen]='\0';
return stringlen;
}
int BerString::print(char *buf, unsigned int len){
int totlenused=0;
/* strings are used for binary data as well as text. We need to handle 8 bit
data as well as normal printable strings */
for(char *cur=str;cur<str+stringlen;cur++){
int lenused=snprintf(buf,len,isprint(*cur)?"%c":"\\0x%02x",
*(unsigned char*)cur);
assert(lenused!=-1);
totlenused+=lenused;
len-=lenused;
buf+=lenused;
}
return totlenused;
}
BerString::BerString(CONST char *strng,unsigned int length):
stringlen(length){
assert(strng);
assert(str=new char[length]);
memcpy(str,strng,length);
}
BerString::BerString(unsigned char *strn): BerBase(strn){ // Wire protocol only
assert(strn[0]==STRING_TAG);
unsigned char headerlen;
unpack_len(strn,headerlen);
// I can use edatacache->len here because BerBase(strn) fills in edatacache.
assert(str=new char[edatacache->Length()-headerlen]);
stringlen=edatacache->Length()-headerlen;
memcpy(str,strn+headerlen,stringlen);
}
BerEncodedData *BerString::encode(){
unsigned char headerlen;
unsigned char *retval=start_data(STRING_TAG,stringlen,headerlen);
memcpy(retval+headerlen,str,stringlen);
return new BerEncodedData(retval,headerlen+stringlen);
}
/*
* BerIPAddr is used to encode an ipaddress.
*/
int BerIPAddr::print(char *buf, unsigned int length){
assert(len==4); // don't know how to deal with v6 yet
// might be a problem with endian but I borrowed this code out of nslookup
return snprintf(buf,length,"%u.%u.%u.%u;",
((unsigned)ipaddr[0] & 0xff),
((unsigned)ipaddr[1] & 0xff),
((unsigned)ipaddr[2] & 0xff),
((unsigned)ipaddr[3] & 0xff));
}
BerIPAddr::BerIPAddr(const char *addr,int length):
len(length){
assert(addr);
assert(len==4); //don't know how to deal with anything but v4
assert(ipaddr=new unsigned char[4]);
memcpy(ipaddr,addr,4);
}
// Wire protocol only
BerIPAddr::BerIPAddr(unsigned char *strn): BerBase(strn),len(4){
assert(strn[0]==IPADDR_TAG);
unsigned char headerlen;
unpack_len(strn,headerlen);
// I can use edatacache->len here because BerBase(strn) fills in edatacache.
assert(edatacache->Length()==6);
assert(ipaddr=new unsigned char[4]);
memcpy(ipaddr,strn+headerlen,4);
}
BerEncodedData *BerIPAddr::encode(){
unsigned char headerlen;
unsigned char *retval=start_data(IPADDR_TAG,4,headerlen);
memcpy(retval+headerlen,ipaddr,4);
return new BerEncodedData(retval,headerlen+len);
}
syntax highlighted by Code2HTML, v. 0.9.1