Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Brian Bruns
3 : * Copyright (C) 2010 Frediano Ziglio
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #if HAVE_STRING_H
24 : #include <string.h>
25 : #endif /* HAVE_STRING_H */
26 :
27 : #include <assert.h>
28 :
29 : #include <freetds/tds.h>
30 : #include <freetds/server.h>
31 : #include <freetds/utils/string.h>
32 : #include <freetds/data.h>
33 : #include <freetds/bytes.h>
34 :
35 : static void
36 2218 : tds_env_change_string(TDSSOCKET * tds, int type, const char *oldvalue, const char *newvalue)
37 : {
38 : TDSFREEZE outer;
39 :
40 2218 : tds_put_byte(tds, TDS_ENVCHANGE_TOKEN);
41 2218 : tds_freeze(tds, &outer, 2);
42 2218 : tds_put_byte(tds, type);
43 2218 : TDS_START_LEN_TINYINT(tds) {
44 2218 : tds_put_string(tds, newvalue, -1);
45 2218 : } TDS_END_LEN_STRING
46 2218 : TDS_START_LEN_TINYINT(tds) {
47 2218 : tds_put_string(tds, oldvalue, -1);
48 2218 : } TDS_END_LEN_STRING
49 2218 : tds_freeze_close(&outer);
50 2218 : }
51 :
52 : void
53 2218 : tds_env_change(TDSSOCKET * tds, int type, const char *oldvalue, const char *newvalue)
54 : {
55 : TDS_SMALLINT totsize;
56 :
57 : /* If oldvalue is NULL, treat it like "" */
58 2218 : if (oldvalue == NULL)
59 734 : oldvalue = "";
60 :
61 : /*
62 : * NOTE: I don't know why each type of environment value has a different
63 : * format. According to the TDS5 specifications, they should all use
64 : * the same format. The code for the TDS_ENV_CHARSET case *should*
65 : * work for all environment values. -- Steve Kirkendall
66 : */
67 :
68 2218 : switch (type) {
69 2218 : case TDS_ENV_DATABASE:
70 : case TDS_ENV_LANG:
71 : case TDS_ENV_PACKSIZE:
72 : case TDS_ENV_CHARSET:
73 2218 : tds_env_change_string(tds, type, oldvalue, newvalue);
74 2218 : break;
75 0 : case TDS_ENV_LCID:
76 : case TDS_ENV_SQLCOLLATION:
77 : #if 1
78 0 : tds_put_byte(tds, TDS_ENVCHANGE_TOKEN);
79 : /* totsize = type + len + newvalue + len + oldvalue */
80 0 : totsize = 3 + strlen(newvalue) + strlen(oldvalue);
81 0 : tds_put_smallint(tds, totsize);
82 0 : tds_put_byte(tds, type);
83 0 : tds_put_byte(tds, strlen(newvalue));
84 0 : tds_put_n(tds, newvalue, strlen(newvalue));
85 0 : tds_put_byte(tds, strlen(oldvalue));
86 0 : tds_put_n(tds, oldvalue, strlen(oldvalue));
87 0 : break;
88 : #endif
89 0 : default:
90 0 : tdsdump_log(TDS_DBG_WARN, "tds_env_change() ignoring unsupported environment code #%d", type);
91 : }
92 2218 : }
93 :
94 : void
95 0 : tds_send_eed(TDSSOCKET * tds, int msgno, int msgstate, int severity, const char *msgtext, const char *srvname,
96 : const char *procname, int line, const char *sqlstate)
97 : {
98 : TDSFREEZE outer;
99 :
100 0 : if (!procname)
101 0 : procname = "";
102 :
103 0 : tds_put_byte(tds, TDS_EED_TOKEN);
104 0 : tds_freeze(tds, &outer, 2);
105 0 : tds_put_int(tds, msgno);
106 0 : tds_put_byte(tds, msgstate);
107 0 : tds_put_byte(tds, severity);
108 0 : TDS_START_LEN_TINYINT(tds) {
109 0 : tds_put_string(tds, sqlstate, -1);
110 0 : } TDS_END_LEN_STRING
111 0 : tds_put_byte(tds, 0); /* has EED */
112 0 : tds_put_byte(tds, 1); /* status */
113 0 : tds_put_byte(tds, 0); /* transaction state */
114 0 : TDS_START_LEN_USMALLINT(tds) {
115 0 : tds_put_string(tds, msgtext, -1);
116 0 : } TDS_END_LEN_STRING
117 0 : TDS_START_LEN_TINYINT(tds) {
118 0 : tds_put_string(tds, srvname, -1);
119 0 : } TDS_END_LEN_STRING
120 0 : TDS_START_LEN_TINYINT(tds) {
121 0 : tds_put_string(tds, procname, -1);
122 0 : } TDS_END_LEN_STRING
123 0 : tds_put_smallint(tds, line); /* line */
124 0 : tds_freeze_close(&outer);
125 0 : }
126 :
127 : static void
128 1660 : tds_send_info(TDSSOCKET * tds, TDS_TINYINT token, int msgno, int msgstate, int severity,
129 : const char *msgtext, const char *srvname, const char *procname, int line)
130 : {
131 : TDSFREEZE outer;
132 :
133 1660 : if (!procname)
134 1660 : procname = "";
135 :
136 1660 : tds_put_byte(tds, token);
137 1660 : tds_freeze(tds, &outer, 2);
138 1660 : tds_put_int(tds, msgno);
139 1660 : tds_put_byte(tds, msgstate);
140 1660 : tds_put_byte(tds, severity);
141 1660 : TDS_START_LEN_USMALLINT(tds) {
142 1660 : tds_put_string(tds, msgtext, -1);
143 1660 : } TDS_END_LEN_STRING
144 1660 : TDS_START_LEN_TINYINT(tds) {
145 1660 : tds_put_string(tds, srvname, -1);
146 1660 : } TDS_END_LEN_STRING
147 1660 : TDS_START_LEN_TINYINT(tds) {
148 1660 : tds_put_string(tds, procname, -1);
149 1660 : } TDS_END_LEN_STRING
150 1660 : if (IS_TDS72_PLUS(tds->conn))
151 224 : tds_put_int(tds, line);
152 : else
153 1436 : tds_put_smallint(tds, line);
154 1660 : tds_freeze_close(&outer);
155 1660 : }
156 :
157 : void
158 1548 : tds_send_msg(TDSSOCKET * tds, int msgno, int msgstate, int severity,
159 : const char *msgtext, const char *srvname, const char *procname, int line)
160 : {
161 1548 : tds_send_info(tds, TDS_INFO_TOKEN, msgno, msgstate, severity, msgtext, srvname, procname, line);
162 1548 : }
163 :
164 : void
165 112 : tds_send_err(TDSSOCKET * tds, int msgno, int msgstate, int severity,
166 : const char *msgtext, const char *srvname, const char *procname, int line)
167 : {
168 112 : tds_send_info(tds, TDS_ERROR_TOKEN, msgno, msgstate, severity, msgtext, srvname, procname, line);
169 112 : }
170 :
171 : void
172 734 : tds_send_login_ack(TDSSOCKET * tds, const char *progname)
173 : {
174 : TDS_UINT ui, version;
175 : TDSFREEZE outer;
176 :
177 734 : tds_put_byte(tds, TDS_LOGINACK_TOKEN);
178 734 : tds_freeze(tds, &outer, 2); /* length of message */
179 734 : if (IS_TDS50(tds->conn)) {
180 0 : tds_put_byte(tds, 5);
181 0 : version = 0x05000000u;
182 : } else {
183 734 : tds_put_byte(tds, 1);
184 : /* see src/tds/token.c */
185 734 : if (IS_TDS74_PLUS(tds->conn)) {
186 : version = 0x74000004u;
187 734 : } else if (IS_TDS73_PLUS(tds->conn)) {
188 : version = 0x730B0003u;
189 718 : } else if (IS_TDS72_PLUS(tds->conn)) {
190 : version = 0x72090002u;
191 718 : } else if (IS_TDS71_PLUS(tds->conn)) {
192 718 : version = tds->conn->tds71rev1 ? 0x07010000u : 0x71000001u;
193 : } else {
194 0 : version = (TDS_MAJOR(tds->conn) << 24) | (TDS_MINOR(tds->conn) << 16);
195 : }
196 : }
197 734 : TDS_PUT_A4BE(&ui, version);
198 734 : tds_put_n(tds, &ui, 4);
199 :
200 734 : TDS_START_LEN_TINYINT(tds) {
201 734 : tds_put_string(tds, progname, -1);
202 734 : } TDS_END_LEN_STRING
203 :
204 : /* server version, always big endian */
205 734 : TDS_PUT_A4BE(&ui, tds->conn->product_version & 0x7fffffffu);
206 734 : tds_put_n(tds, &ui, 4);
207 :
208 734 : tds_freeze_close(&outer);
209 734 : }
210 :
211 : void
212 0 : tds_send_capabilities_token(TDSSOCKET * tds)
213 : {
214 0 : tds_put_byte(tds, TDS_CAPABILITY_TOKEN);
215 0 : tds_put_smallint(tds, 18);
216 0 : tds_put_byte(tds, 1);
217 0 : tds_put_byte(tds, 7);
218 0 : tds_put_byte(tds, 7);
219 0 : tds_put_byte(tds, 97);
220 0 : tds_put_byte(tds, 65);
221 0 : tds_put_byte(tds, 207);
222 0 : tds_put_byte(tds, 255);
223 0 : tds_put_byte(tds, 255);
224 0 : tds_put_byte(tds, 230);
225 0 : tds_put_byte(tds, 2);
226 0 : tds_put_byte(tds, 7);
227 0 : tds_put_byte(tds, 0);
228 0 : tds_put_byte(tds, 0);
229 0 : tds_put_byte(tds, 2);
230 0 : tds_put_byte(tds, 0);
231 0 : tds_put_byte(tds, 0);
232 0 : tds_put_byte(tds, 0);
233 0 : tds_put_byte(tds, 0);
234 0 : }
235 :
236 : /**
237 : * Send a "done" token, marking the end of a table, stored procedure, or query.
238 : * \param tds Where the token will be written to.
239 : * \param token The appropriate type of "done" token for this context:
240 : * TDS_DONE_TOKEN outside a stored procedure,
241 : * TDS_DONEINPROC_TOKEN inside a stored procedure, or
242 : * TDS_DONEPROC_TOKEN at the end of a stored procedure.
243 : * \param flags Bitwise-OR of flags in the "enum tds_end" data type.
244 : * TDS_DONE_FINAL for the last statement in a query,
245 : * TDS_DONE_MORE_RESULTS if not the last statement,
246 : * TDS_DONE_ERROR if the statement had an error,
247 : * TDS_DONE_INXACT if a transaction is pending,
248 : * TDS_DONE_PROC inside a stored procedure,
249 : * TDS_DONE_COUNT if a table was sent (and rows counted)
250 : * TDS_DONE_CANCELLED if the query was canceled, and
251 : * TDS_DONE_EVENT if the token marks an event.
252 : * \param numrows Number of rows, if flags has TDS_DONE_COUNT.
253 : */
254 : void
255 1166 : tds_send_done(TDSSOCKET * tds, int token, TDS_SMALLINT flags, TDS_INT numrows)
256 : {
257 1166 : tds_put_byte(tds, token);
258 1166 : tds_put_smallint(tds, flags);
259 1166 : tds_put_smallint(tds, 2); /* are these two bytes the transaction status? */
260 1166 : if (IS_TDS72_PLUS(tds->conn))
261 448 : tds_put_int8(tds, numrows);
262 : else
263 718 : tds_put_int(tds, numrows);
264 1166 : }
265 :
266 : void
267 766 : tds_send_done_token(TDSSOCKET * tds, TDS_SMALLINT flags, TDS_INT numrows)
268 : {
269 766 : tds_send_done(tds, TDS_DONE_TOKEN, flags, numrows);
270 766 : }
271 :
272 : void
273 0 : tds_send_control_token(TDSSOCKET * tds, TDS_SMALLINT numcols)
274 : {
275 : int i;
276 :
277 0 : tds_put_byte(tds, TDS_CONTROL_FEATUREEXTACK_TOKEN);
278 0 : tds_put_smallint(tds, numcols);
279 0 : for (i = 0; i < numcols; i++) {
280 0 : tds_put_byte(tds, 0);
281 : }
282 0 : }
283 :
284 : static void
285 0 : tds_send_col_name(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
286 : {
287 0 : int col, hdrsize = 0;
288 : TDSCOLUMN *curcol;
289 :
290 0 : tds_put_byte(tds, TDS_COLNAME_TOKEN);
291 0 : for (col = 0; col < resinfo->num_cols; col++) {
292 0 : curcol = resinfo->columns[col];
293 0 : hdrsize += tds_dstr_len(&curcol->column_name) + 1;
294 : }
295 :
296 0 : tds_put_smallint(tds, hdrsize);
297 0 : for (col = 0; col < resinfo->num_cols; col++) {
298 0 : curcol = resinfo->columns[col];
299 0 : tds_put_byte(tds, tds_dstr_len(&curcol->column_name));
300 : /* exclude the null */
301 0 : tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), tds_dstr_len(&curcol->column_name));
302 : }
303 0 : }
304 :
305 : static void
306 0 : tds_send_col_info(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
307 : {
308 0 : int col, hdrsize = 0;
309 : TDSCOLUMN *curcol;
310 :
311 0 : tds_put_byte(tds, TDS_COLFMT_TOKEN);
312 :
313 0 : for (col = 0; col < resinfo->num_cols; col++) {
314 0 : curcol = resinfo->columns[col];
315 0 : hdrsize += 5;
316 0 : if (!is_fixed_type(curcol->column_type)) {
317 0 : hdrsize++;
318 : }
319 : }
320 0 : tds_put_smallint(tds, hdrsize);
321 :
322 0 : for (col = 0; col < resinfo->num_cols; col++) {
323 0 : curcol = resinfo->columns[col];
324 0 : tds_put_n(tds, "\0\0\0\0", 4);
325 0 : tds_put_byte(tds, curcol->column_type);
326 0 : if (!is_fixed_type(curcol->column_type)) {
327 0 : tds_put_byte(tds, curcol->column_size);
328 : }
329 : }
330 0 : }
331 :
332 : static void
333 0 : tds_send_result(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
334 : {
335 : TDSCOLUMN *curcol;
336 : int i, totlen;
337 : size_t len;
338 :
339 0 : tds_put_byte(tds, TDS_RESULT_TOKEN);
340 0 : totlen = 2;
341 0 : for (i = 0; i < resinfo->num_cols; i++) {
342 0 : curcol = resinfo->columns[i];
343 0 : len = tds_dstr_len(&curcol->column_name);
344 0 : totlen += 8;
345 0 : totlen += len;
346 0 : curcol = resinfo->columns[i];
347 0 : if (!is_fixed_type(curcol->column_type)) {
348 0 : totlen++;
349 : }
350 : }
351 0 : tds_put_smallint(tds, totlen);
352 0 : tds_put_smallint(tds, resinfo->num_cols);
353 0 : for (i = 0; i < resinfo->num_cols; i++) {
354 0 : curcol = resinfo->columns[i];
355 0 : len = tds_dstr_len(&curcol->column_name);
356 0 : tds_put_byte(tds, tds_dstr_len(&curcol->column_name));
357 0 : tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), len);
358 0 : tds_put_byte(tds, '0');
359 0 : tds_put_int(tds, curcol->column_usertype);
360 0 : tds_put_byte(tds, curcol->column_type);
361 0 : if (!is_fixed_type(curcol->column_type)) {
362 0 : tds_put_byte(tds, curcol->column_size);
363 : }
364 0 : tds_put_byte(tds, 0);
365 : }
366 0 : }
367 :
368 : static TDSRET
369 176 : tds7_send_result(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
370 : {
371 : int i;
372 :
373 : /* TDS7+ uses TDS7_RESULT_TOKEN to send column names and info */
374 176 : tds_put_byte(tds, TDS7_RESULT_TOKEN);
375 :
376 : /* send the number of columns */
377 176 : tds_put_smallint(tds, resinfo->num_cols);
378 :
379 : /* send info about each column */
380 352 : for (i = 0; i < resinfo->num_cols; i++) {
381 176 : TDSCOLUMN *curcol = resinfo->columns[i];
382 :
383 : /* usertype, flags, and type */
384 176 : if (IS_TDS72_PLUS(tds->conn))
385 176 : tds_put_int(tds, curcol->column_usertype);
386 : else
387 0 : tds_put_smallint(tds, curcol->column_usertype);
388 176 : tds_put_smallint(tds, curcol->column_flags);
389 176 : tds_put_byte(tds, curcol->on_server.column_type);
390 :
391 176 : TDS_PROPAGATE(curcol->funcs->put_info(tds, curcol));
392 :
393 : /* finally the name, in UTF-16 format */
394 176 : TDS_START_LEN_TINYINT(tds) {
395 528 : tds_put_string(tds, tds_dstr_cstr(&curcol->column_name), tds_dstr_len(&curcol->column_name));
396 176 : } TDS_END_LEN_STRING
397 : }
398 : return TDS_SUCCESS;
399 : }
400 :
401 : /**
402 : * Send any tokens that mark the start of a table. This automatically chooses
403 : * the right tokens for this client's version of the TDS protocol. In other
404 : * words, it is a wrapper around tds_send_col_name(), tds_send_col_info(),
405 : * tds_send_result(), and tds7_send_result().
406 : * \param tds The socket to which the tokens will be written. Also,
407 : * it contains the TDS protocol version number.
408 : * \param resinfo Describes the table to be send, especially the number
409 : * of columns and the names & data types of each column.
410 : */
411 : TDSRET
412 176 : tds_send_table_header(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
413 : {
414 176 : switch (TDS_MAJOR(tds->conn)) {
415 0 : case 4:
416 : /*
417 : * TDS4 uses TDS_COLNAME_TOKEN to send column names, and
418 : * TDS_COLFMT_TOKEN to send column info. The number of columns
419 : * is implied by the number of column names.
420 : */
421 0 : tds_send_col_name(tds, resinfo);
422 0 : tds_send_col_info(tds, resinfo);
423 0 : break;
424 :
425 0 : case 5:
426 : /* TDS5 uses a TDS_RESULT_TOKEN to send all column information */
427 0 : tds_send_result(tds, resinfo);
428 0 : break;
429 :
430 176 : case 7:
431 : case 8:
432 : /*
433 : * TDS7+ uses a TDS7_RESULT_TOKEN to send all column
434 : * information.
435 : */
436 176 : return tds7_send_result(tds, resinfo);
437 : break;
438 : }
439 : return TDS_SUCCESS;
440 : }
441 :
442 : TDSRET
443 264 : tds_send_row(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
444 : {
445 : int i;
446 :
447 264 : tds_put_byte(tds, TDS_ROW_TOKEN);
448 528 : for (i = 0; i < resinfo->num_cols; i++) {
449 264 : TDSCOLUMN *curcol = resinfo->columns[i];
450 :
451 264 : TDS_PROPAGATE(curcol->funcs->put_data(tds, curcol, 0));
452 : }
453 : return TDS_SUCCESS;
454 : }
455 :
456 : void
457 16 : tds71_send_prelogin(TDSSOCKET * tds)
458 : {
459 : static const unsigned char prelogin_default[] = {
460 : 0x00, 0x00, 0x1a, 0x00, 0x06,
461 : 0x01, 0x00, 0x20, 0x00, 0x01,
462 : 0x02, 0x00, 0x21, 0x00, 0x01,
463 : 0x03, 0x00, 0x22, 0x00, 0x00,
464 : 0x04, 0x00, 0x22, 0x00, 0x01,
465 : 0xff,
466 : 0x08, 0x00, 0x01, 0x55, 0x00, 0x00,
467 : 0x02,
468 : 0x00,
469 : 0x00
470 : };
471 : unsigned char prelogin[sizeof(prelogin_default)];
472 :
473 16 : memcpy(prelogin, prelogin_default, sizeof(prelogin));
474 16 : TDS_PUT_A4BE(&prelogin[0x1a], tds->conn->product_version & 0x7fffffffu);
475 16 : tds_put_n(tds, prelogin, sizeof(prelogin));
476 16 : }
477 :
|