#include #include #include #include #include #include #include #include #include #include #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;j1;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;curlen 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); }