6165cc1e14c438a7a72fc86c51c16b636bea734b
[strongswan.git] / src / libstrongswan / utils / fetcher.c
1 /**
2 * @file fetcher.c
3 *
4 * @brief Implementation of fetcher_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Andreas Steffen
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <fetcher://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #ifdef LIBCURL
24 #include <curl/curl.h>
25 #endif /* LIBCURL */
26
27 #ifdef LIBLDAP
28 #include <ldap.h>
29 #endif /* LIBLDAP */
30
31 #include <library.h>
32 #include <debug.h>
33
34 #include "fetcher.h"
35
36 typedef struct private_fetcher_t private_fetcher_t;
37
38 /**
39 * @brief Private Data of a fetcher_t object.
40 */
41 struct private_fetcher_t {
42 /**
43 * Public data
44 */
45 fetcher_t public;
46
47 /**
48 * URI of the information source
49 */
50 const char *uri;
51
52 #ifdef LIBCURL
53 /**
54 * we use libcurl from http://curl.haxx.se/ as a fetcher
55 */
56 CURL* curl;
57 #endif /* LIBCURL */
58
59 #ifdef LIBLDAP
60 /**
61 * we use libldap from http://www.openssl.org/ as a fetcher
62 */
63 LDAP *ldap;
64 LDAPURLDesc *lurl;
65 #endif /* LIBLDAP */
66 };
67
68 /**
69 * writes data into a dynamically resizeable chunk_t
70 * needed for libcurl responses
71 */
72 static size_t curl_write_buffer(void *ptr, size_t size, size_t nmemb, void *data)
73 {
74 size_t realsize = size * nmemb;
75 chunk_t *mem = (chunk_t*)data;
76
77 mem->ptr = (u_char *)realloc(mem->ptr, mem->len + realsize);
78 if (mem->ptr) {
79 memcpy(&(mem->ptr[mem->len]), ptr, realsize);
80 mem->len += realsize;
81 }
82 return realsize;
83 }
84
85 /**
86 * Implements fetcher_t.get for curl methods
87 */
88 static chunk_t curl_get(private_fetcher_t *this)
89 {
90 chunk_t response = chunk_empty;
91
92 #ifdef LIBCURL
93 if (this->curl)
94 {
95 CURLcode res;
96 chunk_t curl_response = chunk_empty;
97 char curl_error_buffer[CURL_ERROR_SIZE];
98
99 curl_easy_setopt(this->curl, CURLOPT_URL, this->uri);
100 curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, curl_write_buffer);
101 curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)&curl_response);
102 curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, &curl_error_buffer);
103 curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE);
104 curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, FETCHER_TIMEOUT);
105 curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE);
106
107 DBG1("sending curl request to '%s'...", this->uri);
108 res = curl_easy_perform(this->curl);
109
110 if (res == CURLE_OK)
111 {
112 DBG1("received valid curl response");
113 response = chunk_clone(curl_response);
114 }
115 else
116 {
117 DBG1("curl request failed: %s", curl_error_buffer);
118 }
119 curl_free(curl_response.ptr);
120 }
121 #else
122 DBG1("warning: libcurl fetching not compiled in");
123 #endif /* LIBCURL */
124 return response;
125 }
126
127 /**
128 * Implements fetcher_t.post.
129 */
130 static chunk_t http_post(private_fetcher_t *this, const char *request_type, chunk_t request)
131 {
132 chunk_t response = chunk_empty;
133
134 #ifdef LIBCURL
135 if (this->curl)
136 {
137 CURLcode res;
138 struct curl_slist *headers = NULL;
139 chunk_t curl_response = chunk_empty;
140 char curl_error_buffer[CURL_ERROR_SIZE];
141 char content_type[BUF_LEN];
142
143 /* set content type header */
144 snprintf(content_type, BUF_LEN, "Content-Type: %s", request_type);
145 headers = curl_slist_append(headers, content_type);
146
147 /* set options */
148 curl_easy_setopt(this->curl, CURLOPT_HTTPHEADER, headers);
149 curl_easy_setopt(this->curl, CURLOPT_URL, this->uri);
150 curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, curl_write_buffer);
151 curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, (void *)&curl_response);
152 curl_easy_setopt(this->curl, CURLOPT_POSTFIELDS, request.ptr);
153 curl_easy_setopt(this->curl, CURLOPT_POSTFIELDSIZE, request.len);
154 curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, &curl_error_buffer);
155 curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE);
156 curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, FETCHER_TIMEOUT);
157 curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE);
158
159 DBG1("sending http post request to '%s'...", this->uri);
160 res = curl_easy_perform(this->curl);
161
162 if (res == CURLE_OK)
163 {
164 DBG1("received valid http response");
165 response = chunk_clone(curl_response);
166 }
167 else
168 {
169 DBG1("http post request using libcurl failed: %s", curl_error_buffer);
170 }
171 curl_slist_free_all(headers);
172 curl_free(curl_response.ptr);
173 }
174 #else
175 DBG1("warning: libcurl fetching not compiled in");
176 #endif /* LIBCURL */
177 return response;
178 }
179
180 #ifdef LIBLDAP
181 /**
182 * Parses the result returned by an ldap query
183 */
184 static chunk_t ldap_parse(LDAP *ldap, LDAPMessage *result)
185 {
186 chunk_t response = chunk_empty;
187 err_t ugh = NULL;
188
189 LDAPMessage *entry = ldap_first_entry(ldap, result);
190
191 if (entry != NULL)
192 {
193 BerElement *ber = NULL;
194 char *attr;
195
196 attr = ldap_first_attribute(ldap, entry, &ber);
197
198 if (attr != NULL)
199 {
200 struct berval **values = ldap_get_values_len(ldap, entry, attr);
201
202 if (values != NULL)
203 {
204 if (values[0] != NULL)
205 {
206 response.len = values[0]->bv_len;
207 response.ptr = malloc(response.len);
208 memcpy(response.ptr, values[0]->bv_val, response.len);
209
210 if (values[1] != NULL)
211 {
212 ugh = "more than one value was fetched - first selected";
213 }
214 }
215 else
216 {
217 ugh = "no values in attribute";
218 }
219 ldap_value_free_len(values);
220 }
221 else
222 {
223 ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
224 }
225 ldap_memfree(attr);
226 }
227 else
228 {
229 ugh = ldap_err2string(ldap_result2error(ldap, entry, 0));
230 }
231 ber_free(ber, 0);
232 }
233 else
234 {
235 ugh = ldap_err2string(ldap_result2error(ldap, result, 0));
236 }
237 if (ugh)
238 {
239 DBG1("ldap request failed: %s", ugh);
240 }
241 return response;
242 }
243 #endif /* LIBLDAP */
244
245 /**
246 * Implements fetcher_t.get for curl methods
247 */
248 static chunk_t ldap_get(private_fetcher_t *this)
249 {
250 chunk_t response = chunk_empty;
251
252 #ifdef LIBLDAP
253 if (this->ldap)
254 {
255 err_t ugh = NULL;
256 int rc;
257 int ldap_version = LDAP_VERSION3;
258
259 struct timeval timeout;
260
261 timeout.tv_sec = FETCHER_TIMEOUT;
262 timeout.tv_usec = 0;
263
264 ldap_set_option(this->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
265 ldap_set_option(this->ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
266
267 DBG1("sending ldap request to '%s'...", this->uri);
268
269 rc = ldap_simple_bind_s(this->ldap, NULL, NULL);
270 if (rc == LDAP_SUCCESS)
271 {
272 LDAPMessage *result;
273
274 timeout.tv_sec = FETCHER_TIMEOUT;
275 timeout.tv_usec = 0;
276
277 rc = ldap_search_st(this->ldap, this->lurl->lud_dn,
278 this->lurl->lud_scope,
279 this->lurl->lud_filter,
280 this->lurl->lud_attrs,
281 0, &timeout, &result);
282
283 if (rc == LDAP_SUCCESS)
284 {
285 response = ldap_parse(this->ldap, result);
286 if (response.ptr)
287 {
288 DBG1("received valid ldap response");
289 }
290 ldap_msgfree(result);
291 }
292 else
293 {
294 ugh = ldap_err2string(rc);
295 }
296 }
297 else
298 {
299 ugh = ldap_err2string(rc);
300 }
301 ldap_unbind_s(this->ldap);
302
303 if (ugh)
304 {
305 DBG1("ldap request failed: %s", ugh);
306 }
307 }
308 #else /* !LIBLDAP */
309 DBG1("warning: libldap fetching not compiled in");
310 #endif /* !LIBLDAP */
311 return response;
312 }
313
314 /**
315 * Implements fetcher_t.destroy
316 */
317 static void destroy(private_fetcher_t *this)
318 {
319 #ifdef LIBCURL
320 if (this->curl)
321 {
322 curl_easy_cleanup(this->curl);
323 }
324 #endif /* LIBCURL */
325
326 #ifdef LIBLDAP
327 if (this->lurl)
328 {
329 ldap_free_urldesc(this->lurl);
330 }
331 #endif /* LIBLDAP */
332
333 free(this);
334 }
335
336 /*
337 * Described in header.
338 */
339 fetcher_t *fetcher_create(const char *uri)
340 {
341 private_fetcher_t *this = malloc_thing(private_fetcher_t);
342
343 /* initialize */
344 this->uri = uri;
345
346 #ifdef LIBCURL
347 this->curl = NULL;
348 #endif /* LIBCURL */
349
350 #ifdef LIBLDAP
351 this->lurl = NULL;
352 this->ldap = NULL;
353 #endif /* LIBLDAP */
354
355 if (strlen(uri) >= 4 && strncasecmp(uri, "ldap", 4) == 0)
356 {
357 #ifdef LIBLDAP
358 int rc = ldap_url_parse(uri, &this->lurl);
359
360 if (rc == LDAP_SUCCESS)
361 {
362 this->ldap = ldap_init(this->lurl->lud_host,
363 this->lurl->lud_port);
364 }
365 else
366 {
367 DBG1("ldap: %s", ldap_err2string(rc));
368 this->ldap = NULL;
369 }
370 #endif /* LIBLDAP */
371 this->public.get = (chunk_t (*) (fetcher_t*))ldap_get;
372 }
373 else
374 {
375 #ifdef LIBCURL
376 this->curl = curl_easy_init();
377 if (this->curl == NULL)
378 {
379 DBG1("curl_easy_init_failed()");
380 }
381 #endif /* LIBCURL */
382 this->public.get = (chunk_t (*) (fetcher_t*))curl_get;
383 }
384
385 /* public functions */
386 this->public.post = (chunk_t (*) (fetcher_t*,const char*,chunk_t))http_post;
387 this->public.destroy = (void (*) (fetcher_t*))destroy;
388
389 return &this->public;
390 }
391
392 /**
393 * Described in header.
394 */
395 void fetcher_initialize(void)
396 {
397 #ifdef LIBCURL
398 CURLcode res;
399
400 /* initialize libcurl */
401 DBG1("initializing libcurl");
402 res = curl_global_init(CURL_GLOBAL_NOTHING);
403 if (res != CURLE_OK)
404 {
405 DBG1("libcurl could not be initialized: %s", curl_easy_strerror(res));
406 }
407 #endif /* LIBCURL */
408 }
409
410 /**
411 * Described in header.
412 */
413 void fetcher_finalize(void)
414 {
415 #ifdef LIBCURL
416 /* finalize libcurl */
417 DBG1("finalizing libcurl");
418 curl_global_cleanup();
419 #endif /* LIBCURL */
420 }
421