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) 2005  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 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 
27 #if HAVE_ERRNO_H
28 #include <errno.h>
29 #endif /* HAVE_ERRNO_H */
30 
31 #if HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif /* HAVE_STDLIB_H */
34 
35 #if HAVE_STRING_H
36 #include <string.h>
37 #endif /* HAVE_STRING_H */
38 
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42 
43 #include <freetds/tds.h>
44 #include <freetds/iconv.h>
45 #include <freetds/bytes.h>
46 #include <freetds/stream.h>
47 #include <freetds/checks.h>
48 
49 #if TDS_ADDITIONAL_SPACE < 8
50 #error Not supported
51 #endif
52 
53 /**
54  * \addtogroup network
55  * @{
56  */
57 
58 /*
59  * CRE 01262002 making buf a void * means we can put any type without casting
60  *		much like read() and memcpy()
61  */
62 int
tds_put_n(TDSSOCKET * tds,const void * buf,size_t n)63 tds_put_n(TDSSOCKET * tds, const void *buf, size_t n)
64 {
65 	size_t left;
66 	const unsigned char *bufp = (const unsigned char *) buf;
67 
68 	for (; n;) {
69 		if (tds->out_buf_max <= tds->out_pos) {
70 			tds_write_packet(tds, 0x0);
71 			continue;
72 		}
73 		left = tds->out_buf_max - tds->out_pos;
74 		if (left > n)
75 			left = n;
76 		if (bufp) {
77 			memcpy(tds->out_buf + tds->out_pos, bufp, left);
78 			bufp += left;
79 		} else {
80 			memset(tds->out_buf + tds->out_pos, 0, left);
81 		}
82 		tds->out_pos += (unsigned int)left;
83 		n -= left;
84 	}
85 	return 0;
86 }
87 
88 /**
89  * Output a string to wire
90  * automatic translate string to unicode if needed
91  * \return bytes written to wire
92  * \param tds state information for the socket and the TDS protocol
93  * \param s   string to write
94  * \param len length of string in characters, or -1 for null terminated
95  */
96 int
tds_put_string(TDSSOCKET * tds,const char * s,int len)97 tds_put_string(TDSSOCKET * tds, const char *s, int len)
98 {
99 	int res;
100 	TDSSTATICINSTREAM r;
101 	TDSDATAOUTSTREAM w;
102 	enum TDS_ICONV_ENTRY iconv_entry;
103 
104 	if (len < 0) {
105 		TDS_ENCODING *client;
106 		client = &tds->conn->char_convs[client2ucs2]->from.charset;
107 
108 		if (client->min_bytes_per_char == 1) {	/* ascii or UTF-8 */
109 			len = (int)strlen(s);
110 		} else if (client->min_bytes_per_char == 2) {	/* UCS-2 or variant */
111 			const char *p = s;
112 
113 			while (p[0] || p[1])
114 				p += 2;
115 			len = (int)(p - s);
116 
117 		} else if (client->min_bytes_per_char == 4) {	/* UCS-4 or variant */
118 			const char *p = s;
119 
120 			while (p[0] || p[1] || p[2] || p[3])
121 				p += 4;
122 			len = (int)(p - s);
123 		} else {
124 			assert(client->min_bytes_per_char < 3);	/* FIXME */
125 		}
126 	}
127 
128 	assert(len >= 0);
129 
130 	/* valid test only if client and server share a character set. TODO conversions for Sybase */
131 	if (IS_TDS7_PLUS(tds->conn)) {
132 		iconv_entry = client2ucs2;
133 	} else if (IS_TDS50(tds->conn)) {
134 		iconv_entry = client2server_chardata;
135 	} else {
136 		tds_put_n(tds, s, len);
137 		return len;
138 	}
139 
140 	tds_staticin_stream_init(&r, s, len);
141 	tds_dataout_stream_init(&w, tds);
142 
143 	res = tds_convert_stream(tds, tds->conn->char_convs[iconv_entry], to_server, &r.stream, &w.stream);
144 	return w.written;
145 }
146 
147 int
tds_put_buf(TDSSOCKET * tds,const unsigned char * buf,int dsize,int ssize)148 tds_put_buf(TDSSOCKET * tds, const unsigned char *buf, int dsize, int ssize)
149 {
150 	int cpsize;
151 
152 	cpsize = ssize > dsize ? dsize : ssize;
153 	tds_put_n(tds, buf, cpsize);
154 	dsize -= cpsize;
155 	tds_put_n(tds, NULL, dsize);
156 	return tds_put_byte(tds, cpsize);
157 }
158 
159 int
tds_put_int8(TDSSOCKET * tds,TDS_INT8 i)160 tds_put_int8(TDSSOCKET * tds, TDS_INT8 i)
161 {
162 	TDS_UCHAR *p;
163 
164 	if (tds->out_pos >= tds->out_buf_max)
165 		tds_write_packet(tds, 0x0);
166 
167 	p = &tds->out_buf[tds->out_pos];
168 	TDS_PUT_UA4LE(p, (TDS_UINT) i);
169 	TDS_PUT_UA4LE(p+4, (TDS_UINT) (i >> 32));
170 	tds->out_pos += 8;
171 	return 0;
172 }
173 
174 int
tds_put_int(TDSSOCKET * tds,TDS_INT i)175 tds_put_int(TDSSOCKET * tds, TDS_INT i)
176 {
177 	TDS_UCHAR *p;
178 
179 	if (tds->out_pos >= tds->out_buf_max)
180 		tds_write_packet(tds, 0x0);
181 
182 	p = &tds->out_buf[tds->out_pos];
183 	TDS_PUT_UA4LE(p, i);
184 	tds->out_pos += 4;
185 	return 0;
186 }
187 
188 int
tds_put_smallint(TDSSOCKET * tds,TDS_SMALLINT si)189 tds_put_smallint(TDSSOCKET * tds, TDS_SMALLINT si)
190 {
191 	TDS_UCHAR *p;
192 
193 	if (tds->out_pos >= tds->out_buf_max)
194 		tds_write_packet(tds, 0x0);
195 
196 	p = &tds->out_buf[tds->out_pos];
197 	TDS_PUT_UA2LE(p, si);
198 	tds->out_pos += 2;
199 	return 0;
200 }
201 
202 int
tds_put_byte(TDSSOCKET * tds,unsigned char c)203 tds_put_byte(TDSSOCKET * tds, unsigned char c)
204 {
205 	if (tds->out_pos >= (unsigned int)tds->out_buf_max)
206 		tds_write_packet(tds, 0x0);
207 	tds->out_buf[tds->out_pos++] = c;
208 	return 0;
209 }
210 
211 int
tds_init_write_buf(TDSSOCKET * tds)212 tds_init_write_buf(TDSSOCKET * tds)
213 {
214 	TDS_MARK_UNDEFINED(tds->out_buf, tds->out_buf_max);
215 	tds->out_pos = 8;
216 	return 0;
217 }
218 
219 /**
220  * Flush packet to server
221  * @return TDS_FAIL or TDS_SUCCESS
222  */
223 TDSRET
tds_flush_packet(TDSSOCKET * tds)224 tds_flush_packet(TDSSOCKET * tds)
225 {
226 	TDSRET result = TDS_FAIL;
227 
228 	/* GW added check for tds->s */
229 	if (!IS_TDSDEAD(tds)) {
230 		if (tds->out_pos > tds->out_buf_max) {
231 			result = tds_write_packet(tds, 0x00);
232 			if (TDS_FAILED(result))
233 				return result;
234 		}
235 		result = tds_write_packet(tds, 0x01);
236 	}
237 	return result;
238 }
239 
240 /** @} */
241