#include #include #include #include #include #include #include #include "tdslib.h" #include #include #include #include #include #include #include #include "lasuite.h" static void tdslib_rep_other(int fd, const TDS_LOGIN &login); using namespace std; using namespace boost::gregorian; using namespace std; DEBUG_KEY D_CLIMSG("climsg","Client messages"); DEBUG_KEY D_SRVMSG("srvmsg","Server messages"); DEBUG_KEY D_DATAMSG("datamsg","Server data messages"); DEBUG_KEY D_HDATAMSG("hdatamsg","Server header data messages"); DEBUG_KEY D_BULK ("bulk","Bulk in"); static string toupper(const char *s) { string ret; while (*s != '\0'){ ret += toupper(*s); s++; } return ret; } /* Retourne le nombre de bytes consommés dans le buffer */ int tdslib_process( const char *buf, unsigned bufLen, bool &reterror ) { int ret = 0; int skip = 0; reterror = false; while( 1 ){ int type = ( int ) buf[skip + 0]; int last = ( int ) buf[skip + 1]; int length = ( int ) ( ( unsigned char ) buf[skip + 3] | ( ( unsigned char ) buf[skip + 2] << 8 ) ); #if 0 int chanel = ( int ) ( ( unsigned char ) buf[skip + 5] | ( ( unsigned char ) buf[skip + 4] << 8 ) ); int packetNum = ( int ) ( unsigned char ) buf[skip + 6]; int window = ( int ) ( unsigned char ) buf[skip + 7]; #endif unsigned used = skip + length; if( !( type == 1 || type == 2 || type == 4 || type == 6 || type == 7 || type == 15 ) ){ reterror = true; } if( used > bufLen ){ break; }else if( last ){ if( last > 1 ){ reterror = true; } ret = used; break; }else if( used == bufLen ){ break; }else if( length <= 0 ){ reterror = true; break; }else{ skip += length; } } return ret; } enum PK_TYPE{ PK_4_2_QUERY = 0x01, // TDS 4.2 or 7.0 query PK_4_2_LOGIN = 0x02, // TDS 4.2 or 5.0 login packet PK_ANSWER = 0x04, // responses from server PK_CANCELS = 0x06, // cancels PK_BULKDATA = 0x07, // Bulk data sent from client PK_5_0_QUERY = 0x0F, // TDS 5.0 query PK_7_0_LOGIN = 0x10, // TDS 7.0 login packet }; struct TDS_LOGIN_4_2{ unsigned char type; unsigned char pad1[7]; char host_name[30]; unsigned char host_name_length; char user_name[30]; unsigned char user_name_length; char password[30]; unsigned char password_length; char host_process[30]; unsigned char host_process_length; char magic1[6]; /* mystery stuff */ unsigned char bulk_copy; char magic2[9]; /* mystery stuff */ char app_name[30]; unsigned char app_name_length; char server_name[30]; unsigned char server_name_length; #if 0 210 ? magic3[1] /* 0, don't know this one either */ 211 INT8 password2_length 212 CHAR[30] password2 242 CHAR[223] magic4 465 INT8 password2_length_plus2 466 INT16 major_version /* TDS version */ 468 INT16 minor_version /* TDS version */ 470 CHAR library_name[10] /* "Ct-Library" or "DB-Library" */ 480 INT8 library_length 481 INT16 major_version2 /* program version */ 483 INT16 minor_version2 /* program version */ 485 ? magic6[3] /* ? last two octets are 13 and 17 */ /* bdw reports last two as 12 and 16 here */ /* possibly a bitset flag */ 488 CHAR[30] language /* e.g. "us-english" */ 518 INT8 language_length 519 ? magic7[1] /* mystery stuff */ 520 INT16 old_secure /* explanation? */ 522 INT8 encrypted /* 1 means encrypted all password fields blank */ 523 ? magic8[1] /* no clue... zeros */ 524 CHAR sec_spare[9] /* explanation? */ 533 CHAR[30] char_set /* e.g. "iso_1" */ 563 INT8 char_set_length 564 INT8 magic9[1] /* 1 */ 565 CHAR[6] block_size /* in text */ 571 INT8 block_size_length 572 ? magic10[25] /* lots of stuff here...no clue */ #endif }; enum TDSTYPE{ SYBVOID_7P=0x1F, // SYBIMAGE=0x22, // yes 4 SYBTEXT=0x23, // yes 4 yes SYBUNIQUE=0x24, // 7+ yes 1 SYBVARBINARY=0x25, // yes 1 SYBINTN=0x26, // yes 1 SYBVARCHAR=0x27, // yes 1 SYBBINARY=0x2D, // yes 1 SYBCHAR=0x2F, // yes 1 SYBINT1=0x30, // no 0 SYBBIT=0x32, // no 0 SYBINT2=0x34, // no 0 SYBINT4=0x38, // no 0 SYBDATETIME4=0x3A, // no 0 SYBREAL=0x3B, // no 0 SYBMONEY=0x3C, // no 0 SYBDATETIME=0x3D, // no 0 SYBFLT8=0x3E, // no 0 SYBSINT1=0x40, // 5 no 0 SYBUINT2=0x41, // 5 no 0 SYBUINT4=0x42, // 5 no 0 SYBUINT8=0x43, // 5 no 0 SYBVARIANT=0x62, // 7+ yes 4 SYBNTEXT=0x63, // 7+ yes 4 yes SYBNVARCHAR=0x67, // 7+ yes 1 SYBBITN=0x68, // yes 1 SYBDECIMAL=0x6A, // yes 1 SYBNUMERIC=0x6C, // yes 1 SYBFLTN=0x6D, // yes 1 SYBMONEYN=0x6E, // yes 1 SYBDATETIMN=0x6F, // yes 1 SYBMONEY4=0x7A, // no 0 SYBINT8=0x7F, // no 0 XSYBVARBINARY=0xA5, // 7+ yes 2 * XSYBVARCHAR=0xA7, // 7+ yes 2 * yes XSYBBINARY=0xAD, // 7+ yes 2 XSYBCHAR=0xAF, // 7+ yes 2 yes SYBLONGBINARY=0xE1, // 5 yes 4 XSYBNVARCHAR=0xE7, // 7+ yes 2 * yes XSYBNCHAR=0xEF, // 7+ yes 2 yes }; static map maptypes; static void tdslib_init_maptypes() { if (maptypes.size()==0){ maptypes[0x1F]="SYBVOID"; maptypes[0x22]="SYBIMAGE"; maptypes[0x23]="SYBTEXT"; maptypes[0x24]="SYBUNIQUE"; maptypes[0x25]="SYBVARBINARY"; maptypes[0x26]="SYBINTN"; maptypes[0x27]="SYBVARCHAR"; maptypes[0x2D]="SYBBINARY"; maptypes[0x2F]="SYBCHAR"; maptypes[0x30]="SYBINT1"; maptypes[0x32]="SYBBIT"; maptypes[0x34]="SYBINT2"; maptypes[0x38]="SYBINT4"; maptypes[0x3A]="SYBDATETIME4"; maptypes[0x3B]="SYBREAL"; maptypes[0x3C]="SYBMONEY"; maptypes[0x3D]="SYBDATETIME"; maptypes[0x3E]="SYBFLT8"; maptypes[0x40]="SYBSINT1"; maptypes[0x41]="SYBUINT2"; maptypes[0x42]="SYBUINT4"; maptypes[0x43]="SYBUINT8"; maptypes[0x62]="SYBVARIANT"; maptypes[0x63]="SYBNTEXT"; maptypes[0x67]="SYBNVARCHAR"; maptypes[0x68]="SYBBITN"; maptypes[0x6A]="SYBDECIMAL"; maptypes[0x6C]="SYBNUMERIC"; maptypes[0x6D]="SYBFLTN"; maptypes[0x6E]="SYBMONEYN"; maptypes[0x6F]="SYBDATETIMN"; maptypes[0x7A]="SYBMONEY4"; maptypes[0x7F]="SYBINT8"; maptypes[0xA5]="XSYBVARBINARY"; maptypes[0xA7]="XSYBVARCHAR"; maptypes[0xAD]="XSYBBINARY"; maptypes[0xAF]="XSYBCHAR"; maptypes[0xE1]="SYBLONGBINARY"; maptypes[0xE7]="XSYBNVARCHAR"; maptypes[0xEF]="XSYBNCHAR"; } }; enum RESTYPE{ RES_Param_Format_2=0x20, // 5.0 only RES_Language=0x21, // 5.0 only, client-side RES_OrderBy_2=0x22, // 5.0 only?? RES_Language2=0x23, // This is a query looking like 0x21, but the query has // some user defined tag. Not sure what it is. RES_Row_Format_2=0x61, // 5.0 only RES_Logout=0x71, // 5.0? ct_close(), client-side ? RES_Return_Status=0x79, RES_Process_ID=0x7C, // 4.2 only RES_Cursor_Close=0x80, // 5.0 only RES_Cursor_Delete=0x81, // 5.0 only RES_7_0_Result=0x81, // 7.0 only RES_Cursor_Fetch=0x82, // 5.0 only RES_Cursor_Info=0x83, // 5.0 only RES_Cursor_Open=0x84, // 5.0 only RES_Cursor_Declare=0x86, // 5.0 only RES_7_0_Compute_Result=0x88, // 7.0 only RES_Column_Name=0xA0, // 4.2 only RES_Column_Format=0xA1, // 4.2 only RES_Dynamic_2=0xA3, // 5.0 only RES_Table_names=0xA4, // name of tables in a FOR BROWSE select RES_Column_Info=0xA5, // column information in a FOR BROWSE select RES_Option_Cmd=0xA6, // 5.0 only RES_Compute_Names=0xA7, RES_Compute_Result=0xA8, RES_Order_By=0xA9, RES_Error_Message=0xAA, RES_Info_Message=0xAB, RES_Output_Parameters=0xAC, RES_Login_Acknowledgement=0xAD, RES_Control=0xAE, RES_Data=0xD1, // Row Result RES_Data_Compute=0xD3, // Compute Result RES_Params=0xD7, // 5.0 only RES_Capability=0xE2, // 5.0 only. Information on server RES_Environment_Change=0xE3, // (database change, packet size, etc...) RES_Extended_Error_Message=0xE5, RES_DBRPC=0xE6, // 5.0 only RPC calls RES_Dynamic=0xE7, // 5.0 only RES_Param=0xEC, // Format 5.0 only RES_Authentication=0xED, // 7.0 only RES_Result=0xEE, // Set 5.0 only RES_Result_Set_Done=0xFD, RES_Process_Done=0xFE, RES_Done_inside_Process=0xFF, }; static string tds_copy (const char *val, unsigned len) { string ret(val,len); return ret; } void timestamp( char str[40] ) { time_t now; struct tm *nowtm; now = time( NULL ); nowtm = localtime( &now ); sprintf( str, "%4d-%02d-%02d_%02d:%02d:%02d", ( nowtm->tm_year + 1900 ), ( nowtm->tm_mon + 1 ), nowtm->tm_mday, nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec ); } // Convert an IP from FFXXFFXX format to xxx.xxx.xxx.xxx string static char* ipTranslate( unsigned int Hexa, char* outstr ) { typedef struct ips { unsigned char p4, p3, p2, p1; } ip; ip *i = (ip *) &Hexa; sprintf( outstr, "%d.%d.%d.%d", i->p1, i->p2, i->p3, i->p4 ); return( outstr ); } void tdslib_show( const char *buf, unsigned len, FILE * h_out, long long elapsed, int status, int numRetLines, unsigned int saddr, int sport, int queryNo, const char *comment ) { int type; bool printInfo = true; char timeStr[40]; if(buf != NULL){ type = ( int ) buf[0]; if (type == PK_4_2_LOGIN){ TDS_LOGIN_4_2 *login = (TDS_LOGIN_4_2*)buf; string hostname = tds_copy (login->host_name,login->host_name_length); string username = tds_copy (login->user_name,login->user_name_length); string password = tds_copy (login->password,login->password_length); string host_process = tds_copy (login->host_process,login->host_process_length); string app_name = tds_copy (login->app_name,login->app_name_length); string server_name = tds_copy (login->server_name,login->server_name_length); fprintf (h_out,"host_name %s user %s pass %s process %s app_name %s server %s bulk %d\n" ,hostname.c_str(),username.c_str(),password.c_str() ,host_process.c_str(),app_name.c_str(),server_name.c_str() ,login->bulk_copy); }else{ char typeStr[40]; switch( type ){ case PK_4_2_LOGIN : sprintf( typeStr, "LOGIN 4.2" ); break; case PK_4_2_QUERY : sprintf( typeStr, "QUERY 4.2" ); case PK_5_0_QUERY : sprintf( typeStr, "QUERY 5.0" ); break; case PK_ANSWER : sprintf( typeStr, "ANSWER" ); break; case PK_CANCELS : sprintf( typeStr, "CANCEL" ); break; default : sprintf( typeStr, "type %02x inconnu", buf[0] ); printInfo = false; } int maxsize = 512; int headersize = 8 + 6; int sizetoprint = maxsize - headersize; if( type == PK_4_2_QUERY ){ // in 4.2, even the first buffer has only 8 bytes header headersize = 8; } int numpackets = len / maxsize; if( len % maxsize ) numpackets++; // Start the comment with the timestamp timestamp( timeStr ); fprintf( h_out, "/* %s : ", timeStr ); // If elapsed is given, we have the time it took for the query, // so let's save it in a comment within the log if( elapsed ){ fprintf( h_out, "realtime(%.3f) ", ( elapsed / 1000000.0 ) ); } fprintf( h_out, "--status(%X)-- ", status ); if( numRetLines >= 0 ){ fprintf( h_out, "++numretLines(%d)++ ", numRetLines ); } // IP address and Port char ipstr[16]; fprintf( h_out, " --IP(%s) PORT(%d)-- queryNo(%d) " , ipTranslate(saddr, ipstr), sport, queryNo ); // If we have additionnal comment if( comment ){ fprintf( h_out, " comment[%s] ", comment ); } // What type of packet we have ? fprintf( h_out, " TYPE[%s] ", typeStr ); // Debug info //fprintf( h_out, " len(%d) numpackets(%d)", len, numpackets ); // Close the comment fprintf( h_out, " */\n" ); if( printInfo ){ for( int i = 0; i < numpackets; i++ ){ // Last packet is smaller. if( i == ( numpackets - 1 ) ){ unsigned over = len % maxsize; if(over){ sizetoprint = over - headersize; }else{ sizetoprint -= (headersize); } } fwrite( buf + ( i * maxsize ) + headersize, 1, sizetoprint, h_out ); // After first buffer, readjust the headersize from 12 to 8 if( i == 0 ){ headersize = 8; sizetoprint = maxsize - headersize; } } }else{ fprintf( h_out, "/* Unknowned type, can't print the DATA... */" ); } fprintf( h_out, "\nGO\n\n" ); } }else{ // Start the comment with the timestamp timestamp( timeStr ); fprintf( h_out, "/* %s : EMPTY QUERY ", timeStr ); // If elapsed is given, we have the time it took for the query, // so let's save it in a comment within the log if( elapsed ){ fprintf( h_out, "realtime(%.3f) ", ( elapsed / 1000000.0 ) ); } // IP address and Port char ipstr[16]; fprintf( h_out, " --IP(%s) PORT(%d)-- queryNo(%d) " , ipTranslate(saddr, ipstr), sport, queryNo ); fprintf( h_out, " comment[%s] ", comment ); // Close the comment fprintf( h_out, " */\n\n" ); } fflush( h_out ); } void tdslib_debug (DEBUG_KEY &dkey, unsigned id, const TDS_LOGIN &login, const char *ctl, ...) { char tmp[20000]; int n = snprintf (tmp,sizeof(tmp)-1,"%05d %-20s %-20s %-20s",id,login.user.c_str(),login.host.c_str(),login.program.c_str()); va_list list; va_start (list,ctl); vsnprintf (tmp+n,sizeof(tmp)-n-1,ctl,list); va_end (list); debug_printf (dkey,"%s",tmp); } struct TDS_POINTER{ const char *buf; unsigned pos; unsigned len; unsigned endblock; TDS_POINTER (const char *_buf, unsigned _len){ buf = _buf; len = _len; pos = 15; // First data block has a 15 bytes header // next blocks have a 8 bytes header endblock = 512; if (len < 512) endblock = len; } bool ended(){ return pos >= len; } void nextchar(){ pos++; if (pos < len && pos == endblock){ pos += 8; endblock += 512; if (endblock > len) endblock = len; } } unsigned char getuchar (){ unsigned char ret = (unsigned char)buf[pos]; nextchar(); return ret; } char getchar (){ char ret = buf[pos]; nextchar(); return ret; } int getint2(){ unsigned u1 = getuchar(); unsigned u2 = getuchar(); return u2*256+u1; } unsigned getuint2(){ unsigned u1 = getuchar(); unsigned u2 = getuchar(); return u2*256+u1; } int getint4(){ union{ unsigned char c[4]; int val; }u; u.c[0] = getuchar(); u.c[1] = getuchar(); u.c[2] = getuchar(); u.c[3] = getuchar(); return u.val; } unsigned getuint4(){ union{ unsigned char c[4]; unsigned val; }u; u.c[0] = getuchar(); u.c[1] = getuchar(); u.c[2] = getuchar(); u.c[3] = getuchar(); return u.val; } string getstring(int l){ string ret; for (int i=0; i= 32 && cr < 127) ? (char)cr : '.'; c++; if (c == 16){ tmptxt[c] = '\0'; tdslib_debug (dkey,id,login,"dump %s %s\n",tmphex,tmptxt); tmptxt[0] = '\0'; tmphex[0] = '\0'; c=0; } } if (c > 0){ tmptxt[c] = '\0'; tdslib_debug (dkey,id,login,"dump %-48s %s\n",tmphex,tmptxt); } } static void tdslib_dumphex (DEBUG_KEY &dkey, unsigned id, const TDS_LOGIN &login, TDS_POINTER ptr) { char tmphex[24*3+1],tmptxt[24+1]; tmphex[0] = '\0'; tmptxt[0] = '\0'; int c = 0; while (!ptr.ended()){ unsigned char cr = ptr.getuchar(); sprintf (tmphex+3*c,"%02x ",cr); tmptxt[c] = (cr >= 32 && cr < 127) ? (char)cr : '.'; c++; if (c == 16){ tmptxt[c] = '\0'; tdslib_debug (dkey,id,login,"dump %s %s\n",tmphex,tmptxt); tmptxt[0] = '\0'; tmphex[0] = '\0'; c=0; } } if (c > 0){ tmptxt[c] = '\0'; tdslib_debug (dkey,id,login,"dump %-48s %s\n",tmphex,tmptxt); } } void tdslib_dumphex0 (DEBUG_KEY &dkey, unsigned id, const TDS_LOGIN &login, int used, const char *ptb) { TDS_POINTER ptr (ptb,used); tdslib_dumphex (dkey,id,login,ptr); } /* TDS records are copied in 512 chunks. Each chunk has its header. tdslib_process knows about this and simply packs all the chunk together. Now we extract the data by skipping the headers. */ static void tdslib_copychunk ( string &sql, const char *buf, int len, int headsize) { sql = ""; unsigned off = 0; while (len > 0){ if (len <= 512){ sql += string(buf+off+headsize,len-headsize); break; }else{ sql += string(buf+off+headsize,512-headsize); off += 512; len -= 512; headsize = 8; } } } /* Analyse a TDS client request */ TDS_REQTYPE tdslib_parsereq (const char *buf, int len, unsigned id, TDS_LOGIN &login, TDS_SQL &sql, BULK_RECORDS &brecs) { TDS_REQTYPE ret = TDS_REQ_NONE; PK_TYPE type = (PK_TYPE) buf[0]; tdslib_debug (D_CLIMSG,id,login,"PK_TYPE %d\n",type); if (type == PK_4_2_LOGIN){ TDS_LOGIN_4_2 *plogin = (TDS_LOGIN_4_2*)buf; login.user = tds_copy (plogin->user_name,plogin->user_name_length); login.password = tds_copy (plogin->password,plogin->password_length); login.host = tds_copy (plogin->host_name,plogin->host_name_length); login.process = tds_copy (plogin->host_process,plogin->host_process_length); login.program = tds_copy (plogin->app_name,plogin->app_name_length); login.server = tds_copy (plogin->server_name,plogin->server_name_length); ret = TDS_REQ_LOGIN; }else if (type == PK_4_2_QUERY){ tdslib_dumphex (D_CLIMSG,id,login,len,buf); tdslib_copychunk (sql.sql,buf,len,8); //tdslib_show (glocal.s->query,used,stdout,0,0,0,0,0,1,"test"); ret = TDS_REQ_SQL; }else if (type == PK_5_0_QUERY){ tdslib_dumphex (D_CLIMSG,id,login,len,buf); if (buf[8] == RES_Option_Cmd){ ret = TDS_REQ_OPTIONS; }else if (buf[8] == RES_Language){ if (len <= 14){ tdslib_debug (D_CLIMSG,id,login,"tdslib_parsereq PK_5_0_QUERY: len < 14: %d\n",len); sql.sql = ""; }else{ tdslib_copychunk (sql.sql,buf,len,14); } //tdslib_show (glocal.s->query,used,stdout,0,0,0,0,0,1,"test"); ret = TDS_REQ_SQL; }else if (buf[8] == RES_Language2){ // Ok here we are in the dark. The layout seems to be // (starting at position 9) length of sub-packet // 3 zeros, length of user defined tag // tag // 01 00 // length of query (4 bytes little endian) // query // and the rest, no idea unsigned tag_len = buf[13]; const char *ptsize = buf + 14 + tag_len + 2; unsigned querysize = ptsize[0] + ptsize[1]*256; tdslib_copychunk (sql.sql,ptsize+4,querysize,0); //fprintf (stderr,"RES_Language2 tag_len=%u querysize=%u :%s:\n",tag_len,querysize,sql.sql.c_str()); ret = TDS_REQ_SQL; }else if (buf[8] == RES_Logout){ ret = TDS_REQ_LOGOUT; }else{ tlmp_error ("PK_5_0_QUERY buf[8]=%02x\n",buf[8]); } }else if (type == PK_CANCELS){ tdslib_dumphex (D_CLIMSG,id,login,len,buf); ret = TDS_REQ_CANCEL; }else if (type == PK_BULKDATA){ tdslib_dumphex (D_CLIMSG,id,login,len,buf); TDS_POINTER tdsptr (buf,len); tdsptr.pos = 8; unsigned nb = brecs.fields.size(); while (!tdsptr.ended()){ //unsigned size = buf[pos]+buf[pos+1]*256; unsigned char marker = tdsptr.getuchar(); if (marker == 0x7) break; tdsptr.skip(3); string bulk; vector rec; for (unsigned i=0; i 0){ int len = (endbuffer - d)-1; int len1 = len - bf.decimals; rec.push_back(string_f("%*.*s.%s",len1,len1,d,d+len1)); }else{ rec.push_back (d); } }else{ rec.push_back("???"); tdsptr.skip(bf.size); } bulk += string_f ("\t%s",rec[rec.size()-1].c_str()); } debug_printf (D_BULK,"%s\n",bulk.c_str()); brecs.recs.push_back(rec); } ret = TDS_REQ_BULK; } return ret; } struct COLUMN_TYPE{ string name; unsigned flag; unsigned dbtype; unsigned tdstype; unsigned subtype; unsigned column_size; unsigned decimals; unsigned tlength; int table_length; string table; COLUMN_TYPE(){ flag = 0; dbtype = 0; tdstype = 0; column_size = 0; tlength=0; table_length = 0; subtype = 0; decimals = 0; } }; static void data_printf (FILE *f, const char *ctl, ...) { if (f != NULL){ va_list list; va_start (list,ctl); vfprintf (f,ctl,list); va_end (list); } } /* Analyse a TDS server response. Return the status of the request. */ int tdslib_parseres( const char *buf, unsigned len, int &numrows, // How many rows returned by the server unsigned id, const TDS_LOGIN &login, BULK_RECORDS &brecs, FILE *datalog) // Record data content in this log { int ret = -1; tdslib_init_maptypes(); unsigned restype = buf[8]; data_printf (datalog,"DBG: %d restype = %02x len=%u\n",id,restype,len); numrows = -1; TDS_POINTER tdsptr(buf,len); while (restype == RES_Done_inside_Process){ printf ("done_inside_process skip 9\n"); //tdslib_dumphex (D_SRVMSG,id,login,len,buf); tdsptr.skip(9); buf += 9; restype = buf[8]; len -= 9; } if (restype == RES_Result_Set_Done){ tdslib_dumphex (D_SRVMSG,id,login,9+8,buf); numrows = (unsigned int ) ( ( ( unsigned char ) buf[len - 4] ) | ( ( unsigned char ) buf[len - 3] << 8 ) | ( ( unsigned char ) buf[len - 2] << 16 ) | ( ( unsigned char ) buf[len - 1] << 24 ) ); data_printf (datalog,"RES: %d %d\n",id,numrows); if (len == 17) return 0; tdsptr.skip(9); buf += 9; len -= 9; restype = buf[8]; } if (restype == RES_Environment_Change){ tdslib_dumphex (D_SRVMSG,id,login,len,buf); ret = 0; }else if (restype == RES_Row_Format_2){ int nbcol = (unsigned char)buf[13]; //tdslib_dumphex (D_HDATAMSG,id,login,len,buf); tdslib_dumphex (D_DATAMSG,id,login,tdsptr); int column = 0; #if 0 //int type = ( int ) buf[skip + 0]; int last = ( int ) buf[skip + 1]; //int length = ( int ) ( ( unsigned char ) buf[skip + 3] | // ( ( unsigned char ) buf[skip + 2] << 8 ) ); // int chanel = (int)((unsigned char)buf[skip+5] // | ((unsigned char)buf[skip+4] << 8)); // int packetNum = (int)(unsigned char)buf[skip+6]; // int window = (int)(unsigned char)buf[skip+7]; unsigned pt = skip+dataoff; unsigned end_block = skip + 512; if (end_block > len) end_block = len; unsigned field_len = 0; #endif data_printf (datalog,"DEF: %d nbcol=%d\n",id,nbcol); vector row; while(!tdsptr.ended()){ COLUMN_TYPE col; while (true){ unsigned field_len = tdsptr.getuchar(); if (field_len > 0){ char car = tdsptr.getchar(); if (car == 0) break; col.name += "."; col.name += car; for (unsigned i=1; i 128) col.column_size = 128; //skip2 = tdsptr.getuchar(); col.table_length = tdsptr.getint2(); col.table = tdsptr.getstring(col.table_length); }else if (col.tdstype == SYBIMAGE){ col.column_size = tdsptr.getuint4(); col.table_length = tdsptr.getint2(); col.table = tdsptr.getstring(col.table_length); }else if (col.tdstype == SYBCHAR){ col.column_size = tdsptr.getuchar(); }else if (col.tdstype == SYBNUMERIC){ col.subtype = tdsptr.getuchar(); col.column_size = tdsptr.getuchar(); col.decimals = tdsptr.getuchar(); }else if (col.tdstype == SYBDATETIMN){ col.column_size = tdsptr.getuchar(); }else if (col.tdstype == SYBINTN){ col.column_size = tdsptr.getuchar(); }else if (col.tdstype == SYBBINARY || col.tdstype == SYBVARBINARY){ col.column_size = tdsptr.getuchar(); }else if (col.tdstype == SYBLONGBINARY){ col.column_size = tdsptr.getuint4(); } unsigned char skip3 = tdsptr.getuchar(); data_printf (datalog,"DBG: %d %s\n",id,col.name.c_str()); // Find the field name const char *name = strrchr(col.name.c_str(),'.'); if (name == NULL) name = col.name.c_str(); if (datalog != NULL){ char tmpname[strlen(name)+1]; strcpy (tmpname,name); #if 0 char *pt = tmpname; while (*pt != '\0'){ char car = *pt; car = tolower(car); *pt++ = car; } #endif data_printf (datalog,"DEF: %d %-20s dbtype=%02x tdstype=%02x(%u),%s column-size=%u decimals=%u tlength=%d skip=%u,%u,%u sk=%u,%u,%u %s\n" ,id,tmpname ,col.dbtype,col.tdstype,col.subtype,maptypes[col.tdstype].c_str() ,col.column_size,col.decimals,col.table_length ,skip1,skip2,skip3,sk[0],sk[1],sk[2],col.table.c_str()); } row.push_back(col); column++; if (column == nbcol){ //data_printf (datalog,"\n"); column = 0; break; } } //tdsptr.skip(1); unsigned nexttype = tdsptr.getuchar(); if (nexttype == 0) nexttype = tdsptr.getuchar(); data_printf (datalog,"DBG: %d nexttype=%02x,%s\n",id,nexttype,maptypes[nexttype].c_str()); unsigned car=0xff; if (nexttype == RES_Control){ int nbf = tdsptr.getint2(); data_printf (datalog,"DBG: %d nbf=%d\n",id,nbf); car = tdsptr.getuchar(); while (car == 0) car = tdsptr.getuchar(); // Skip the zeros, why ? } if (car == RES_Order_By){ int nb = tdsptr.getint2(); tdsptr.skip(nb); car = tdsptr.getuchar(); } data_printf (datalog,"DBG: %d car=%x %x %x\n",id,car,RES_Data,RES_Result_Set_Done); bool ok = true; while (ok && car == RES_Data){ data_printf (datalog,"DATA: %d ",id); for (unsigned i=0; i 0){ static double tb[]={0,10,100,1000,10000,100000,1000000,10000000,100000000}; data_printf (datalog,"%lf",(double)(total/tb[col.decimals])); }else{ data_printf (datalog,"%Ld",total); } } } break; case SYBDATETIMN: { unsigned len = tdsptr.getuchar(); if (len == 0){ data_printf (datalog,"NULL"); }else if (len == 4){ unsigned c1 = tdsptr.getuchar(); unsigned c2 = tdsptr.getuchar(); unsigned c3 = tdsptr.getuchar(); unsigned c4 = tdsptr.getuchar(); unsigned minutes = c4*256+c3; unsigned hours = minutes/60; minutes %= 60; unsigned days = c2*256+c1; date d(from_simple_string("1900-01-01")); d += date_duration(days); data_printf (datalog,"%u/%02d/%02d %02u:%02u",(unsigned)d.year(),(unsigned)d.month(),(unsigned)d.day(),hours,minutes); }else if (len == 8){ unsigned c1 = tdsptr.getuchar(); unsigned c2 = tdsptr.getuchar(); unsigned c3 = tdsptr.getuchar(); unsigned c4 = tdsptr.getuchar(); unsigned c5 = tdsptr.getuchar(); unsigned c6 = tdsptr.getuchar(); unsigned c7 = tdsptr.getuchar(); unsigned c8 = tdsptr.getuchar(); // time of day encoded in 1/300 of a second unsigned minutes = ((c8<<24)|(c7<<16)|(c6<<8)|c5)/(300*60); unsigned hours = minutes/60; minutes %= 60; unsigned days = (c4<<24)|(c3<<16)|(c2<<8)|c1; date d(from_simple_string("1900-01-01")); d += date_duration(days); data_printf (datalog,"%u/%02d/%02d %02u:%02u",(unsigned)d.year(),(unsigned)d.month(),(unsigned)d.day(),hours,minutes); }else{ for (unsigned i=0; icur->copyto = fd; HANDLE_INFO *n = new HANDLE_INFO; n->type = TYPE_SERVER; n->copyto = priv->cur_fd; n->data = data; priv->c->inject (fd,n); priv->c->setrawmode(fd,true); priv->c->settcpnodelay(priv->cur_fd,true); priv->c->settcpnodelay(fd,true); priv->c->setmonitormode(fd,true); } ARRAY_OBJ *_F_tdsserv::iter_init(unsigned &id, TDS_LOGIN &login) { ARRAY_OBJ *ret = NULL; void *data; int fd = priv->c->iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ id = n->id; login = n->login; ret = n->data; break; } fd = priv->c->iter_next (data); } return ret; } ARRAY_OBJ *_F_tdsserv::iter_next(unsigned &id, TDS_LOGIN &login) { ARRAY_OBJ *ret = NULL; void *data; int fd = priv->c->iter_next(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ id = n->id; login = n->login; ret = n->data; break; } fd = priv->c->iter_next (data); } return ret; } void _F_tdsserv::copy(class _F_TCPSERVER_V1 *s, int from_fd, int to_fd, unsigned id, TDS_LOGIN &login, const void *buf, int len, TDSSERV_INFO &info) { } void _F_tdsserv::login(class _F_TCPSERVER_V1 *s, int fd, unsigned id, TDS_LOGIN &login, TDSSERV_INFO &info, bool &endclient) { } void _F_tdsserv::reqsql(class _F_TCPSERVER_V1 *s, int fd, unsigned id, TDS_LOGIN &login, const char *sql, BULK_RECORDS &brecs, TDSSERV_INFO &info, bool &endclient) { } void _F_tdsserv::bulk(class _F_TCPSERVER_V1 *s, int fd, unsigned id, TDS_LOGIN &login, BULK_RECORDS &brecs, TDSSERV_INFO &info, bool &endclient) { } void _F_tdsserv::options(class _F_TCPSERVER_V1 *s, int fd, unsigned id, TDS_LOGIN &login, const char *txt, TDSSERV_INFO &info, bool &endclient) { } void _F_tdsserv::cancel(class _F_TCPSERVER_V1 *s, int fd, unsigned id, TDS_LOGIN &login, TDSSERV_INFO &info, bool &endclient) { } void _F_tdsserv::serverres(class _F_TCPSERVER_V1 *s, int server_fd, int client_fd, unsigned id, TDS_LOGIN &login, BULK_RECORDS &brecs, TDSSERV_INFO &info, bool &endclient) { } void _F_tdsserv::quit(class _F_TCPSERVER_V1 *s) { } int tdsserv( _F_tdsserv &c, const char *progname, const char *bind, const char* port, const char *unixsock, const char *user, bool daemon, const char *pidfile) { glocal const char *progname = progname; glocal _F_tdsserv *c = &c; glocal _F_tdsserv_private priv; c.priv = &glocal.priv; (bind,port,5); glocal.priv.cur_fd = no; glocal.priv.c = this; HANDLE_INFO *c = new HANDLE_INFO; info.data = c; if (strncmp(info.port,"unix:",5)==0){ c->type = TYPE_CONTROL; }else{ c->type = TYPE_CLIENT; c->id = alloc_uniqid++; setrawmode (true); glocal.priv.cur = c; TDSSERV_INFO sinfo; sinfo.ipaddr = from; glocal.c->connect(this,no,endclient,sinfo); c->data = sinfo.data; } glocal.priv.cur_fd = no; HANDLE_INFO *n = (HANDLE_INFO*)info.data; glocal.priv.cur = n; TDSSERV_INFO sinfo; sinfo.data = n->data; if (n->type == TYPE_CLIENT){ glocal.c->closesource (this,n->copyto,n->id,n->login,sinfo); closeclient (n->copyto); }else if (n->type == TYPE_SERVER){ glocal.c->closeserver(this,no,sinfo); closeclient (n->copyto); } glocal HANDLE_INFO *n = (HANDLE_INFO*)info.data; glocal TDSSERV_INFO sinfo; glocal.sinfo.data = glocal.n->data; glocal.priv.cur = glocal.n; glocal.priv.cur_fd = no; glocal bool *endclient = &endclient; debug_printf ("Rec no=%d type=%d copy=%d len=%d\n",no,glocal.n->type,glocal.n->copyto,info.linelen); if (glocal.n->type == TYPE_CONTROL){ vector words; int n = str_splitline (line,' ',words); if (n == 1 && words[0] == "status"){ sendf ("%s version %s\n",glocal.progname,VERSION); void *data; int fd = iter_init(data); int nbclient = 0; while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ nbclient++; } fd = iter_next (data); } sendf ("nbclient=%d\n",nbclient); }else if (n == 1 && words[0] == "connections"){ void *data; int fd = iter_init(data); while (fd != -1){ HANDLE_INFO *n = (HANDLE_INFO*)data; if (n->type == TYPE_CLIENT){ sendf ("fd=%d host %s program %s process %s user %s\n",fd ,n->login.host.c_str() ,n->login.program.c_str(),n->login.process.c_str() ,n->login.user.c_str()); } fd = iter_next (data); } }else if (n == 1 && words[0] == "quit"){ glocal.c->quit(this); endserver = true; }else if (glocal.c->control(this,words,line)==-1){ sendf ("Invalid request\n"); endclient = true; } sendf ("Ok\n"); }else if (glocal.n->type == TYPE_CLIENT){ glocal.c->copy (this,glocal.priv.cur_fd,glocal.n->copyto,glocal.n->id,glocal.n->login,line,info.linelen,glocal.sinfo); (glocal.n->buf,line,info.linelen); bool error = false; int used = tdslib_process ((const char *)buf,len,error); tdslib_debug (D_CLIMSG,glocal.n->id,glocal.n->login,"CLI len=%d used=%d error=%d type=%d\n",len,used,error,(*(const char *)buf)); if (error){ tdslib_dumphex (D_CLIMSG,glocal.n->id,glocal.n->login,used,(const char *)buf); tdslib_rep_action(glocal.priv.cur_fd,glocal.n->login,0); }else if (used > 0){ TDS_SQL sql; BULK_RECORDS &brecs = glocal.n->brecs; TDS_REQTYPE type = tdslib_parsereq((const char*)buf,used,glocal.n->id,glocal.n->login,sql,brecs); if (type == TDS_REQ_SQL){ glocal.c->reqsql(&glocal.TCPSERVER,glocal.priv.cur_fd,glocal.n->id,glocal.n->login,sql.sql.c_str(),brecs,glocal.sinfo,*glocal.endclient); }else if (type == TDS_REQ_OPTIONS){ glocal.c->options(&glocal.TCPSERVER,glocal.priv.cur_fd,glocal.n->id,glocal.n->login,"",glocal.sinfo,*glocal.endclient); }else if (type == TDS_REQ_CANCEL){ glocal.c->cancel(&glocal.TCPSERVER,glocal.priv.cur_fd,glocal.n->id,glocal.n->login,glocal.sinfo,*glocal.endclient); }else if (type == TDS_REQ_LOGIN){ glocal.c->login(&glocal.TCPSERVER,glocal.priv.cur_fd,glocal.n->id,glocal.n->login,glocal.sinfo,*glocal.endclient); }else if (type == TDS_REQ_LOGOUT){ tdslib_rep_other(glocal.priv.cur_fd,glocal.n->login); *glocal.endclient = true; }else if (type == TDS_REQ_BULK){ // For now, writetext that does nothing // so we provide an answer here. glocal.c->bulk(&glocal.TCPSERVER,glocal.priv.cur_fd,glocal.n->id,glocal.n->login,brecs,glocal.sinfo,*glocal.endclient); brecs.recs.clear(); } brecs.recs.clear(); } return used; }else if (glocal.n->type == TYPE_SERVER){ HANDLE_INFO *ns = (HANDLE_INFO*)getclientdata(glocal.n->copyto); glocal.c->serverres(this,glocal.priv.cur_fd,glocal.n->copyto,ns->id,ns->login,ns->brecs,glocal.sinfo,*glocal.endclient); } int ret = -1; if (o.is_ok()){ string tmp = string ("unix:") + unixsock; if (o.listen("",tmp.c_str())==-1){ tlmp_error ("Can't setup the control socket %s (%s), ending\n" ,unixsock,strerror(errno)); exit (-1); }else{ if (daemon) daemon_init (pidfile,user); if (user != NULL){ struct passwd *u = getpwnam(user) ; if (u == NULL){ tlmp_error ("Error retrieving %s uid/gid\n",user) ; }else{ chown(unixsock, u->pw_uid, u->pw_gid) ; chmod(unixsock, 0770) ; } } } o.loop(); ret = 0; } c.priv = NULL; return ret; } #if 0 // Old table done by hand unsigned char tbstorage[]={ 2, 2,2,3,3,4, 4,4,5,5,6, 6,6,7,7,8, 8,9,9,9,10, 10,11,11,11,11, 11,11,12,12,12, 12,12,12,12,12, 12,12,12,12,12, 12,12,12,12,12, 12,12,12,12,12 }; #endif // Table produced by the storage.cc program // Just execute the program without argument unsigned char tbstorage[]={ 1, 2,2,3,3,4, 4,4,5,5,6, 6,6,7,7,8, 8,9,9,9,10, 10,11,11,11,12, 12,13,13,14,14, 14,15,15,16,16, 16,17 }; #define MAX_PRECISION 37 /* TDS is written in chunks of 512 bytes. One TDS transaction may contain several chunks holding the results of several SQL queries. The last chunk has byte 1 set to 1 to indicate this is tbe last. Otherwise it is 0. The last query ends with a status of 0x10 instead of 0x11. Addendum: It seems that if the status has an odd number, it is not the last. So far, we have 0x11 at the end of a select and 0x10 for the last and 0x3 for a send_msg or 0x2 for the last send_msg. While we are filling a chunk, we keep reference to the last place where we have written a status (0x11). When we are sure this is the last (putlast) query, we flip this 0x11 to 0x10. But this status may be located in the previous chunk that we filled. So we have to keep around the previous filled chunk to be able to patch. See the flush function below where we switch between lastbuf and curbuf. */ struct BUF512{ char buf[512]; unsigned pos; unsigned last_status_pos; void reset(){ pos = 8; last_status_pos = 0; } BUF512(){ reset(); } void writeheader(unsigned char type, bool last){ buf[0] = type; buf[1] = last ? 1 : 0; buf[2] = pos/256; buf[3] = pos%256; buf[4] = 0; buf[5] = 0; buf[6] = 0; buf[7] = 0; } void patchstatus(){ // printf ("patchstatus %u pos=%u\n",last_status_pos,pos); if (last_status_pos > 0){ buf[last_status_pos] &= 0xfe; // Remove bit 0 } } void write (int fd){ if (pos > 8) ::write (fd,buf,pos); } }; static bool alldigit (const char *pt, int len) { bool ret = true; for (int i=0; ireset(); lastbuf->reset(); fd = _fd; } void flush(){ curbuf->writeheader(type,true); curbuf->patchstatus(); lastbuf->patchstatus(); lastbuf->write(fd); curbuf->write(fd); } void check(){ // Make sure there is room for one more byte if (curbuf->pos == 512){ lastbuf->write(fd); curbuf->writeheader(type,false); // Switch the buffers BUF512 *tmp = curbuf; curbuf = lastbuf; lastbuf = tmp; curbuf->reset(); } } void putuchar(unsigned char v){ check(); curbuf->buf[curbuf->pos++] = v; } void putchar(char v){ check(); curbuf->buf[curbuf->pos++] = v; } void putuchars(unsigned char *v, int size){ for (int i=0; i>8)&0xff); putuchar((v>>16)&0xff); putuchar(v>>24); } void putuint5(unsigned long long v){ union { unsigned long long v; char s[8]; }u; u.v = v; putuchar(u.s[0]); putuchar(u.s[1]); putuchar(u.s[2]); putuchar(u.s[3]); putuchar(u.s[4]); } void putuint8(unsigned long long v){ putuchar(v&0xff); putuchar((v>>8)&0xff); putuchar((v>>16)&0xff); putuchar((v>>24)&0xff); putuchar((v>>32)&0xff); putuchar((v>>40)&0xff); putuchar((v>>48)&0xff); putuchar((v>>56)&0xff); } void putuint2_be(unsigned long long v){ putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint3_be(unsigned long long v){ putuchar((v>>16)&0xff); putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint4_be(unsigned long long v){ putuchar((v>>24)&0xff); putuchar((v>>16)&0xff); putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint5_be(unsigned long long v){ putuchar((v>>32)&0xff); putuchar((v>>24)&0xff); putuchar((v>>16)&0xff); putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint6_be(unsigned long long v){ // Big endian version putuchar((v>>40)&0xff); putuchar((v>>32)&0xff); putuchar((v>>24)&0xff); putuchar((v>>16)&0xff); putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint7_be(unsigned long long v){ // Big endian version putuchar((v>>48)&0xff); putuchar((v>>40)&0xff); putuchar((v>>32)&0xff); putuchar((v>>24)&0xff); putuchar((v>>16)&0xff); putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint8_be(unsigned long long v){ // Big endian version putuchar((v>>56)&0xff); putuchar((v>>48)&0xff); putuchar((v>>40)&0xff); putuchar((v>>32)&0xff); putuchar((v>>24)&0xff); putuchar((v>>16)&0xff); putuchar((v>>8)&0xff); putuchar(v&0xff); } void putuint9_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint10_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint11_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[10]); putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint12_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[11]); putuchar(u.s[10]); putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint13_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[12]); putuchar(u.s[11]); putuchar(u.s[10]); putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint14_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[13]); putuchar(u.s[12]); putuchar(u.s[11]); putuchar(u.s[10]); putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint15_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[14]); putuchar(u.s[13]); putuchar(u.s[12]); putuchar(u.s[11]); putuchar(u.s[10]); putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putuint16_be(__uint128_t v){ // Big endian version union { __uint128_t v; char s[16]; }u; u.v = v; putuchar(u.s[15]); putuchar(u.s[14]); putuchar(u.s[13]); putuchar(u.s[12]); putuchar(u.s[11]); putuchar(u.s[10]); putuchar(u.s[9]); putuchar(u.s[8]); putuchar(u.s[7]); putuchar(u.s[6]); putuchar(u.s[5]); putuchar(u.s[4]); putuchar(u.s[3]); putuchar(u.s[2]); putuchar(u.s[1]); putuchar(u.s[0]); } void putlast (unsigned char status, int numrows){ putuchar (RES_Result_Set_Done); putuchar (status); curbuf->last_status_pos = curbuf->pos-1; lastbuf->last_status_pos = 0; // We invalidate this last status position putuchar (0); putuchar (2); // translate putuchar (0); putuint4 (numrows); } void putstring(const char *s){ putuchar(strlen(s)); while (*s != '\0') putuchar(*s++); } void putlongstring(const char *s){ putuint2(strlen(s)); while (*s != '\0') putuchar(*s++); } void login_acknowledge(bool success){ putuchar (RES_Login_Acknowledgement); putuint2 (0x14); // Pre-computed length (sql server) putuchar (success ? 5 : 6); // Sucess or failure putuint4(5); // Protocol Version putstring("sql server"); putuchar(12); putuchar(5); putuchar(0); putuchar(0); // Sybase version } void put_environment_change(const char *txt1, const char *txt2, unsigned char code){ putuchar(RES_Environment_Change); putuint2 (1+1+strlen(txt1)+1+strlen(txt2)); putuchar(code); putstring(txt1); putstring(txt2); } void put_character_set(const char *txt1, const char *txt2){ put_environment_change(txt1,txt2,3); } void put_language(const char *txt1, const char *txt2){ put_environment_change(txt1,txt2,2); } void put_block_size(int size){ char tmp[10]; snprintf (tmp,sizeof(tmp)-1,"%d",size); put_environment_change(tmp,"",4); } void put_extended_error_message(unsigned msg_number, const char *msg1, const char *msg2, const char *msg3, unsigned char state, unsigned char level){ putuchar(RES_Extended_Error_Message); putuint2(7+1+strlen(msg1)+3+2+strlen(msg2)+1+strlen(msg3)+2); putuint4(msg_number); putuchar(state); // state putuchar(level); // Level putstring(msg1); putuchar(0); putuchar(1); putuchar(0); putlongstring(msg2); putstring(msg3); putuint2(0); // Line number putuchar(0); } void put_extended_error_message(const char *msg1, const char *msg2, const char *msg3, unsigned char state, unsigned char level){ put_extended_error_message(0x1648,msg1,msg2,msg3,state,level); } void put_extended_error_message(unsigned msg_number, const char *msg, const TDS_LOGIN &login, unsigned char state, unsigned char level){ put_extended_error_message(msg_number,"ZZZZZ",msg,login.server.c_str(),state,level); } void put_capability(){ putuchar(RES_Capability); static unsigned char tb[]={0x18,0x00,0x01,0x0a,0x01,0x86,0x08,0x33 ,0x61,0x41,0xff,0xff,0xff,0xe6,0x02,0x0a,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00}; for (unsigned i=0; i &defs, const SQL_OPTS &opts, const TDS_LOGIN &login){ putuchar(RES_Row_Format_2); unsigned size=0x10*defs.size()+2; for (unsigned i=0; i= 256) size += 3; }else if (f.type == MYSQL_TYPE_MEDIUM_BLOB || f.type == MYSQL_TYPE_LONG_BLOB || f.type == MYSQL_TYPE_BLOB){ size += 5 + f.table.size(); } } putuint2(size); putuint2(0); putuint2(defs.size()); for (unsigned i=0; i MAX_PRECISION) precision = MAX_PRECISION; if (f.type == MYSQL_TYPE_NEWDECIMAL){ precision--; if (f.decimals > 0) precision--; } putuchar (tbstorage[precision]); putuchar (precision); // Precision putuchar (f.decimals); // Decimals putuchar (0); } break; case MYSQL_TYPE_INT24: break; case MYSQL_TYPE_DATETIME: putuint4 (0xc); putuchar (SYBDATETIMN); putuchar (8); putuchar (0); break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: #if MARIADB_NEW == 1 case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_TIME2: #endif // tempo putuint4 (0x99); putuchar (SYBVARCHAR); putuint2 (f.val1); break; case MYSQL_TYPE_VARCHAR: putuint4 (0x2a); putuchar (SYBVARCHAR); putuint2 (f.val1); break; case MYSQL_TYPE_BIT: putuint4(0x10); putuchar (SYBBIT); putuchar (0); break; case MYSQL_TYPE_ENUM: break; case MYSQL_TYPE_SET: break; case MYSQL_TYPE_TINY_BLOB: break; case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: { if (f.binary){ putuint4 (0x14); putuchar(SYBIMAGE); }else{ putuint4 (0x13); putuchar(SYBTEXT); } const unsigned max_int = (1l<<31)-10; unsigned len = f.val1 < max_int ? f.val1 : max_int; if (len >= opts.textsize) len = opts.textsize; putuint4(len); putlongstring (toupper(f.table.c_str()).c_str()); putuchar (0); } break; case MYSQL_TYPE_VAR_STRING: //putuint4 (f.not_null ? 1 : 2); if (f.val1 < 256){ if (f.binary){ putuint4 (3); putuchar (f.not_null ? SYBBINARY : SYBVARBINARY); }else{ putuint4 (2); putuchar (f.not_null ? SYBCHAR : SYBVARCHAR); } putuint2 (f.val1); }else{ putuint4 (2); if (f.binary){ putuchar (SYBLONGBINARY); }else{ putuchar (XSYBCHAR); } putuint4 (f.val1); putuchar (0); } break; case MYSQL_TYPE_STRING: if (f.binary){ putuint4 (3); putuchar (f.not_null ? SYBBINARY : SYBVARBINARY); putuint2 (f.val1); }else{ putuint4 (1); putuchar (f.not_null ? SYBCHAR : SYBVARCHAR); putuint2 (f.val1); } break; case MYSQL_TYPE_GEOMETRY: break; } } putuchar(RES_Control); putuint2(defs.size()); for (unsigned i=0; i &defs, const SQL_OPTS &opts, const TDS_LOGIN &login, vector &bfields){ bfields.clear(); putuchar(RES_Result); #if 0 unsigned size=10*16+12+21*defs.size(); for (unsigned i=0; i 0) precision--; } bf.size = tbstorage[precision]; bf.size2 = f.val1-2; // Mysql size include the dot and the minus sign // so a 10,2 ends up being 12,2 bf.decimals = f.decimals; } break; case MYSQL_TYPE_INT24: break; case MYSQL_TYPE_DATETIME: bf.type = SYBDATETIME; bf.size = 8; bf.is_char = true; break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: #if MARIADB_NEW == 1 case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_TIME2: #endif bf.type = SYBVARCHAR; bf.type = SYBCHAR; bf.size = f.val1; bf.is_char = true; break; case MYSQL_TYPE_VARCHAR: bf.type = SYBVARCHAR; bf.size = f.val1; bf.is_char = true; break; case MYSQL_TYPE_BIT: bf.type = SYBBIT; bf.size = 1; break; case MYSQL_TYPE_ENUM: break; case MYSQL_TYPE_SET: break; case MYSQL_TYPE_TINY_BLOB: break; case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: { if (f.binary){ bf.type = SYBIMAGE; }else{ bf.type = SYBTEXT; } const unsigned max_int = (1l<<31)-10; unsigned len = f.val1 < max_int ? f.val1 : max_int; if (len >= opts.textsize) len = opts.textsize; bf.size = len; bf.is_char = true; } break; case MYSQL_TYPE_VAR_STRING: if (f.val1 < 256){ if (f.binary){ bf.type = f.not_null ? SYBBINARY : SYBVARBINARY; }else{ bf.type = f.not_null ? SYBCHAR : SYBVARCHAR; } bf.size = f.val1; }else{ if (f.binary){ bf.type = SYBLONGBINARY; }else{ bf.type = XSYBCHAR; } bf.size = f.val1; } bf.is_char = true; break; case MYSQL_TYPE_STRING: if (f.binary){ bf.type = f.not_null ? SYBBINARY : SYBVARBINARY; bf.size = f.val1; }else{ bf.type = f.not_null ? SYBCHAR : SYBVARCHAR; bf.size = f.val1; } bf.is_char = true; break; case MYSQL_TYPE_GEOMETRY: break; } if (bf.type == SYBVARCHAR){ bf.size_min = 0; size_2_max += 5; } if (bf.size_min == (unsigned)-1) bf.size_min = bf.size; size_2_min += bf.size_min; size_2_max += bf.size; bfields.push_back (bf); } unsigned pos_2 = 2; for (unsigned i=0; i &defs, const char *vals[], const SQL_OPTS &opts){ putuchar(RES_Data); for (unsigned i=0; i MAX_PRECISION) precision = MAX_PRECISION; if (f.type == MYSQL_TYPE_NEWDECIMAL){ precision--; if (f.decimals > 0) precision--; } unsigned storage = tbstorage[precision]; unsigned char sign_byte=0; if (val[0] == '-'){ sign_byte = 1; val++; } { nval = 0; const char *pt = val; while (*pt != '\0' && *pt != '.'){ nval = nval*10 + (*pt-'0'); pt++; } nval *= tbdec[f.decimals]; if (*pt == '.'){ __int128_t fval = 0; pt++; int nb = 0; while (*pt != '\0'){ fval = fval*10+(*pt-'0'); pt++; nb++; } nval += fval*tbdec[f.decimals-nb]; } //nval = atof(val)*tbdec[f.decimals]+0.5; } //printf ("new_decimal val=%s nval=%Ld storage=%d\n",val,(unsigned long long) nval,storage); putuchar(storage); // X bytes + sign putuchar(sign_byte); switch (storage){ case 2: putuchar(nval); break; case 3: putuint2_be(nval); break; case 4: putuint3_be(nval); break; case 5: putuint4_be(nval); break; case 6: putuint5_be(nval); break; case 7: putuint6_be(nval); break; case 8: putuint7_be(nval); break; case 9: putuint8_be(nval); break; case 10: putuint9_be(nval); break; case 11: putuint10_be(nval); break; case 12: putuint11_be(nval); break; case 13: putuint12_be(nval); break; case 14: putuint13_be(nval); break; case 15: putuint14_be(nval); break; case 16: putuint15_be(nval); break; case 17: putuint16_be(nval); break; default: tlmp_error ("TDS protocal: storage %d precision %d not done\n",storage,precision); } } } break; case MYSQL_TYPE_INT24: break; case MYSQL_TYPE_DATETIME: { // Encode SYBDATETIMN if (val==NULL){ putuchar(0); }else if (strcmp(val,"0000-00-00 00:00:00")==0){ // PATCH, SYBDATETIMN does not support 0000-00-00 00:00:00 putuchar(8); putuchar(0); putuchar(0); putuchar(0); putuchar(0); putuchar(0); putuchar(0); putuchar(0); putuchar(0); }else{ putuchar(8); date d2(from_simple_string(val)); date d1(from_simple_string("1900-01-01")); date_duration dd = d2-d1; unsigned days = dd.days(); putuchar(days&0xff); putuchar(days>>8); putuchar(days>>16); putuchar(days>>24); int hour = atoi(val+11); int minu = atoi(val+14); int secs = atoi(val+17); unsigned minutes = ((hour*60+minu)*60+secs)*300; putuchar(minutes&0xff); putuchar((minutes>>8)&0xff); putuchar((minutes>>16)&0xff); putuchar(minutes>>24); } } break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: #if MARIADB_NEW == 1 case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_TIME2: #endif case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_STRING: if (val==NULL){ putuchar(0); }else if (f.binary){ putuchar(f.val1); for (unsigned i=0; i 1900 && year < 2200 && month > 0 && month < 13 && day > 0 && day < 32){ snprintf (tmpval,sizeof(tmpval),"%04d.%02d.%02d",year,month,day); val = tmpval; } } if (len > opts.textsize) len = opts.textsize; putuint4(len); for (unsigned i=0; iput_database_context (login); w->putlast (0,0); } /* Send an answer to a action request (not a select). (like create, delete, update, ...) */ void tdslib_rep_other(TDS_WRITER *w, const TDS_LOGIN &login) { int status = 0x11; // Will be changed for 0x10 if last result w->putlast (status,0); } static void tdslib_rep_other(int fd, const TDS_LOGIN &login) { TDS_WRITER w(PK_ANSWER,fd); tdslib_rep_other (&w,login); w.flush(); } /* Send an answer to a action request (not a select). (like create, delete, update, ...) */ void tdslib_rep_action(TDS_WRITER *w, const TDS_LOGIN &login, int nbrows) { int status = 0x11; // Will be changed for 0x10 if last result w->putlast (status,nbrows); } void tdslib_rep_action(int fd, const TDS_LOGIN &login, int nbrows) { TDS_WRITER w(PK_ANSWER,fd); tdslib_rep_action(&w,login,0); w.flush(); } /* Send an answer to a action request (not a select). (like create, delete, update, ...) */ void tdslib_rep_cancel(TDS_WRITER *w, const TDS_LOGIN &login) { w->putlast (0x30,0); } /* Send an answer about a failed SQL request */ void tdslib_err_query(TDS_WRITER *w, const TDS_LOGIN &login, const char *msg, int errcode) { w->put_extended_error_message(errcode,msg,login,2,0xb); // Temp. Not sure it is needed w->put_database_context (login); w->putlast (2,0); } void tdslib_send_msg(TDS_WRITER *w, const TDS_LOGIN &login, const char *msg) { if(msg != NULL){ w->put_extended_error_message(0,msg,login,2,0); w->put_database_context (login); w->putlast (0x3,0); }else{ w->putlast (0x1,0); } } TDS_WRITER *tdslib_getwriter(int fd) { return new TDS_WRITER(PK_ANSWER,fd); } void tdslib_freewriter(TDS_WRITER *w) { w->flush(); delete w; } /* Send the row format of an SQL request */ void tdslib_rep_query_format(TDS_WRITER *w, const TDS_LOGIN &login,vector &defs, const SQL_OPTS &opts) { w->put_row_format(defs,opts,login); } void tdslib_rep_bulk_format(TDS_WRITER *w, const TDS_LOGIN &login,vector &defs, const SQL_OPTS &opts, vector &bfields) { w->put_bulk_format(defs,opts,login,bfields); } /* Send one row of an SQL request */ void tdslib_rep_query_row (TDS_WRITER *w, const TDS_LOGIN &login,vector &defs, const char *vals[], const SQL_OPTS &opts) { w->put_field_row(defs,vals,opts); } void tdslib_rep_query_end (TDS_WRITER *w, unsigned long numrows, const SQL_OPTS &opts) { int status = 0x11; // Will be changed to 0x10 if last result w->putlast (opts.nocount ? 0x1 : status,numrows); } void tdslib_rep_bulk_end (TDS_WRITER *w, unsigned long numrows, const SQL_OPTS &opts) { int status = 0x5; // Will be changed to 0x4 if last result w->putlast (opts.nocount ? 0x1 : status,numrows); } /* Return the current time in micro-seconds */ long long tdslib_getnow () { struct timeval tv; gettimeofday (&tv,NULL); return tv.tv_sec *(long long)1000000 + tv.tv_usec; } void tdslib_logerr ( FILE *flogerr, unsigned id, TDS_LOGIN &login, const char *msg, const char *sql, const char *sybsql) { if (flogerr != NULL){ char date[40]; timestamp(date); fprintf (flogerr,"***%05u %s,%s,%s,%s,%s db=%s datet=%s %s\n%s\n",id ,login.user.c_str(),login.program.c_str(),login.process.c_str() ,login.host.c_str() ,login.server.c_str(),login.cur_db.c_str() ,date ,msg,sql); if (sybsql != NULL) fprintf (flogerr,"!\n%s\n",sybsql); fflush (flogerr); } } void tdslib_logsql ( FILE *flogsql, unsigned id, TDS_LOGIN &login, long long start, long numrows, const char *sql) { if (flogsql != NULL){ char date[40]; timestamp(date); double duration = (tdslib_getnow() - start)/1000000.0; fprintf (flogsql,"***%05u %s,%s rows=%ld exectime=%-7.4lf datet=%s\n %s\n" ,id,login.user.c_str(),login.program.c_str() ,numrows,duration,date,sql); fflush (flogsql); } } FILE_LOG::FILE_LOG() { fsession = NULL; ferror = NULL; fsql = NULL; fdata = NULL; fspc = NULL; } static void closeone(FILE *&f) { if (f != NULL){ fclose (f); f = NULL; } } void FILE_LOG::closeall() { closeone (fsession); closeone (ferror); closeone (fsql); closeone (fdata); closeone (fspc); } FILE_LOG::~FILE_LOG() { closeall(); } FILE *FILE_LOG::open (const char *title, const char *fname) { FILE *ret = NULL; if (fname != NULL){ ret = fopen (fname,"a"); if (ret == NULL){ tlmp_error ("Can't open %s file %s (%s)\n",title,fname,strerror(errno)); } } return ret; } void FILE_LOG::fflush() { if (fsession != NULL) ::fflush (fsession); if (ferror != NULL) ::fflush (ferror); if (fsql != NULL) ::fflush (fsql); if (fdata != NULL) ::fflush (fdata); if (fspc != NULL) ::fflush (fspc); } /* Record the translation from user defined type (Sybase) to MySQL The strings are formatted: user=mysql */ void tdslib_setusertypes (const vector &usertypes) { for (unsigned i=0; i void tdslib_loadusertypes(const char *fusertypes) { if (fusertypes != NULL){ (fusertypes,':',6); SSTRING f4(fields[4]); f4.strip_end(); SSTRING f5(fields[5]); f5.strip_end(); string mytype = parser_cnvsybtype(f5.c_str()); //fprintf (stderr,"user :%s: :%s: -> %s\n",f4.c_str(),f5.c_str(),mytype.c_str()); parser_setusertype (f4.c_str(),mytype.c_str()); return 0; } }