[go: up one dir, main page]

File: sql.c

package info (click to toggle)
specter 1.3%2B1.4pre2-2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 728 kB
  • ctags: 531
  • sloc: ansic: 5,060; sh: 267; makefile: 238; perl: 169
file content (322 lines) | stat: -rw-r--r-- 8,629 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* sql.c
 *
 * common interface for sql-like database output plugins
 * 
 * (C) 2004 by Michal Kwiatkowski <ruby@joker.linuxstuff.pl>
 */

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 
 *  as published by the Free Software Foundation
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <specter/specter.h>
#include "lret.h" /* we need IS_VALID() macro */
#include "sql.h"

#ifdef IP_AS_STRING
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif


/***
 * alloc_sql_insert - create static part of insert command
 * @fields: array of pointers to fields names
 * @table: name of table to insert data into
 * @ret_buff: pointer to fill
 * @size: pointer to number of bytes to allocate, or 0 if it should
 *        be dynamically calculated (result will be stored here)
 * @ret_field: initialized linked list of sql_field structures
 *
 * Returns a pointer to the last character of command or NULL on error.
 */
char *alloc_sql_insert(const char *fields[], const char *table,
		char **ret_buff, size_t *size, struct sql_field **ret_field)
{
	size_t size_min, size_avg;
	char *buff_cur, *buff_end;
	char tmp[SPECTER_IRET_NAME_LEN];
	struct sql_field *f;

	if (fields == NULL) {
		specter_log(SPECTER_FATAL, "SQL table empty. Aborting...\n");
		return NULL;
	}

	/* allocate field structures trying to calculate size of buffer */
	memset(tmp, 0x0, SPECTER_IRET_NAME_LEN);
	*ret_field = NULL;
	/* constant string "INSERT INTO %table () VALUES ()" */
	size_min = size_avg = (25 + strlen(table));
	do {
		char *underscore;
		specter_iret_t *iret;

		strncpy(tmp, *fields, SPECTER_IRET_NAME_LEN-1);
		/* replace first underscore with a dot */
		if ((underscore = strchr(tmp, '_')) != NULL)
			*underscore = '.';
		
		if ((iret = find_iret(tmp)) == NULL) {
			specter_log(SPECTER_DEBUG, "Couldn't find \"%s\" field.\n", tmp);
			continue;
		}

		specter_log(SPECTER_DEBUG, "Field \"%s\" found.\n", tmp);

		/* add field's name and a comma */
		size_min += strlen(*fields) + 1;
		size_avg += strlen(*fields) + 1;

		/* add to size averange length of argument */
		switch (iret->type) {
			case SPECTER_IRET_INT8:
				size_min += 2;
				size_avg += 4; /* -128 */
				break;
			case SPECTER_IRET_INT16:
				size_min += 4;
				size_avg += 6; /* -32768 */
				break;
			case SPECTER_IRET_INT32:
				size_min += 8;
				size_avg += 11; /* -2147483648 */
				break;
			case SPECTER_IRET_INT64:
				size_min += 16;
				size_avg += 20; /* -9223372036854775808 */
				break;
			case SPECTER_IRET_UINT8:
				size_min += 2;
				size_avg += 3; /* 255 */
				break;
			case SPECTER_IRET_UINT16:
				size_min += 4;
				size_avg += 5; /* 65535 */
				break;
			case SPECTER_IRET_IPADDR:
#ifdef IP_AS_STRING
				size_min += 12;
				size_avg += 15; /* "255.255.255.255" */
				break;
#endif
			/* fallthrough when logging ip as ui32 */
			case SPECTER_IRET_UINT32:
				size_min += 8;
				size_avg += 10; /* 4294967295 */
				break;
			case SPECTER_IRET_UINT64:
				size_min += 16;
				size_avg += 20; /* 18446744073709551615 */
				break;
			case SPECTER_IRET_BOOL:
				size_min++;
				size_avg++;
				break;
			case SPECTER_IRET_STRING:
				size_min += 64;
				size_avg += 128;
				break;
			case SPECTER_IRET_RAW:
				specter_log(SPECTER_FATAL, "RAW output not supported.\n");
				goto out_free_fields;
			default:
				specter_log(SPECTER_FATAL,
					"Unknown iret type 0x%x for key \"%s\".\n",
					iret->type, iret->name);
				goto out_free_fields;
		}

		f = (struct sql_field *) malloc(sizeof(struct sql_field));
		if (f == NULL) {
			specter_log(SPECTER_FATAL,
					"Couldn't allocated space for sql_field structure: %s.\n",
					strerror(errno));
			goto out_free_fields;
		}
		strncpy(f->name, *fields, SPECTER_IRET_NAME_LEN-1);
		f->iret = iret;
		f->next = *ret_field;
		*ret_field = f;
	} while (*++fields);

	if (*size == 0)
		*size = size_avg;

	if (*size < size_min) {
		specter_log(SPECTER_FATAL, "SQL buffer too small.\n");
		goto out_free_fields;
	}

	/* done calculating, let's allocate */
	*ret_buff = (char *) malloc(*size);
	if (!*ret_buff) {
		specter_log(SPECTER_FATAL, "Couldn't allocate %u bytes for SQL buffer: %s.\n",
				*size, strerror(errno));
		goto out_free_fields;
	}
	specter_log(SPECTER_DEBUG, "Allocated %u bytes for SQL buffer.\n", *size);
	buff_cur = *ret_buff;
	buff_end = buff_cur + *size;

	buff_cur += snprintf(buff_cur, buff_end-buff_cur, "INSERT INTO %s (", table);

	for (f = *ret_field; f != NULL; f = f->next) {
		buff_cur += snprintf(buff_cur, buff_end-buff_cur, "%s,", f->name);
	}

	/* erase comma */
	buff_cur += snprintf(buff_cur-1, buff_end-buff_cur+1, ") VALUES (");

	return buff_cur-1;

out_free_fields:
	while (*ret_field) {
		f = (*ret_field)->next;
		free(*ret_field);
		*ret_field = f;
	}
	return NULL;
}

/***
 * fill_sql_insert - create dynamic part of insert command
 * @fields: linked list of field to insert
 * @buff: character array to fill with query
 * @length: size of @buff
 * @escape: pointer to function which does proper strings escaping
 *
 * escape(dest, src, n) should done quoting as well and return number
 * of characters written.
 *
 * Returns a pointer to the last character of command or NULL on error.
 */
char *fill_sql_insert(const struct sql_field *f, char *buff,
		const size_t length, size_t (*escape)(char *, const char *, size_t))
{
	char *buff_end = buff + length - 1;

	do {
		if (!f->iret || !IS_VALID(f->iret)) {
			strncpy(buff, "NULL,", buff_end - buff);
			buff += 5;
			continue;
		}

		switch (f->iret->type) {
			case SPECTER_IRET_INT8:
				buff += snprintf(buff, buff_end-buff,
						"%i,", f->iret->value.i8);
				break;
			case SPECTER_IRET_INT16:
				buff += snprintf(buff, buff_end-buff,
						"%i,", f->iret->value.i16);
				break;
			case SPECTER_IRET_INT32:
				buff += snprintf(buff, buff_end-buff,
						"%i,", f->iret->value.i32);
				break;
			case SPECTER_IRET_INT64:
				buff += snprintf(buff, buff_end-buff,
						"%lli,", f->iret->value.i64);
				break;
			case SPECTER_IRET_UINT8:
				buff += snprintf(buff, buff_end-buff,
						"%u,", f->iret->value.ui8);
				break;
			case SPECTER_IRET_UINT16:
				buff += snprintf(buff, buff_end-buff,
						"%u,", f->iret->value.ui16);
				break;
			case SPECTER_IRET_IPADDR:
#ifdef IP_AS_STRING
				buff += escape(buff,
					inet_ntoa((struct in_addr){ntohl(f->iret->value.ui32)}),
					buff_end-buff);
				if (buff <= buff_end)
					buff += snprintf(buff, buff_end-buff, ",");
				break;
#endif
			/* fallthrough when logging ip as ui32 */
			case SPECTER_IRET_UINT32:
				buff += snprintf(buff, buff_end-buff,
						"%u,", f->iret->value.ui32);
				break;
			case SPECTER_IRET_UINT64:
				buff += snprintf(buff, buff_end-buff,
						"%llu,", f->iret->value.ui64);
				break;
			case SPECTER_IRET_BOOL:
				buff += snprintf(buff, buff_end-buff,
						"%i,", f->iret->value.b);
				break;
			case SPECTER_IRET_STRING:
				/* check if string is empty */
				if (((char *)f->iret->value.ptr)[0] == '\0') {
					strncpy(buff, "NULL", 4);
					buff += 4;
				} else {
					buff += escape(buff, f->iret->value.ptr, buff_end-buff);
				}
				if (buff <= buff_end)
					buff += snprintf(buff, buff_end-buff, ",");
				break;
			case SPECTER_IRET_RAW:
				/* FIXME: log as hexadecimal string? */
				specter_log(SPECTER_NOTICE, "RAW output not supported.\n");
				return NULL;
			default:
				specter_log(SPECTER_NOTICE,
					"Unknown iret type 0x%x for key \"%s\".\n",
					f->iret->type, f->iret->name);
				return NULL;
		}

		if (buff > buff_end) {
			specter_log(SPECTER_NOTICE,
				"SQL buffer too small. Insert aborted.\n");
			return NULL;
		}
	} while ((f = f->next) != NULL);

	/* stealing last comma */
	strcpy(buff-1, ")");

	return buff;
}

/***
 * free_sql_insert - deallocate all structures related to sql command
 * @buff: pointer to command buffer
 * @f: linked list of sql_field structures to free()
 */
void free_sql_insert(char *buff, struct sql_field *f)
{
	struct sql_field *next;

	while (f) {
		next = f->next;
		free(f);
		f = next;
	}

	free(buff);
}