ld_cmd.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /*
  2. * $Id$
  3. *
  4. * LDAP Database Driver for SER
  5. *
  6. * Copyright (C) 2008 iptelorg GmbH
  7. *
  8. * This file is part of SER, a free SIP server.
  9. *
  10. * SER is free software; you can redistribute it and/or modify it under the
  11. * terms of the GNU General Public License as published by the Free Software
  12. * Foundation; either version 2 of the License, or (at your option) any later
  13. * version.
  14. *
  15. * SER is distributed in the hope that it will be useful, but WITHOUT ANY
  16. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  17. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  18. * details.
  19. *
  20. * You should have received a copy of the GNU General Public License along
  21. * with this program; if not, write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  23. */
  24. /** \addtogroup ldap
  25. * @{
  26. */
  27. /** \file
  28. * Implementation of functions related to database commands.
  29. */
  30. #include "ld_cmd.h"
  31. #include "ld_fld.h"
  32. #include "ld_con.h"
  33. #include "ld_mod.h"
  34. #include "ld_uri.h"
  35. #include "ld_cfg.h"
  36. #include "ld_res.h"
  37. #include "../../mem/mem.h"
  38. #include "../../dprint.h"
  39. #include "../../ut.h"
  40. #include <string.h>
  41. /** Destroys a ld_cmd structure.
  42. * This function frees all memory used by ld_cmd structure.
  43. * @param cmd A pointer to generic db_cmd command being freed.
  44. * @param payload A pointer to ld_cmd structure to be freed.
  45. */
  46. static void ld_cmd_free(db_cmd_t* cmd, struct ld_cmd* payload)
  47. {
  48. db_drv_free(&payload->gen);
  49. if (payload->result) pkg_free(payload->result);
  50. pkg_free(payload);
  51. }
  52. static int build_result_array(char*** res, db_cmd_t* cmd)
  53. {
  54. struct ld_fld* lfld;
  55. char** t;
  56. int i;
  57. if (cmd->result_count == 0) {
  58. *res = NULL;
  59. return 0;
  60. }
  61. t = (char**)pkg_malloc(sizeof(char*) * (cmd->result_count + 1));
  62. if (t == NULL) {
  63. ERR("ldap: No memory left\n");
  64. return -1;
  65. }
  66. t[cmd->result_count] = NULL;
  67. for(i = 0; i < cmd->result_count; i++) {
  68. lfld = DB_GET_PAYLOAD(cmd->result + i);
  69. /* Attribute names are always zero terminated */
  70. t[i] = lfld->attr.s;
  71. }
  72. *res = t;
  73. return 0;
  74. }
  75. int ld_cmd(db_cmd_t* cmd)
  76. {
  77. struct ld_cmd* lcmd;
  78. struct ld_cfg* cfg;
  79. struct ld_fld* lfld;
  80. int i, j;
  81. lcmd = (struct ld_cmd*)pkg_malloc(sizeof(struct ld_cmd));
  82. if (lcmd == NULL) {
  83. ERR("ldap: No memory left\n");
  84. goto error;
  85. }
  86. memset(lcmd, '\0', sizeof(struct ld_cmd));
  87. if (db_drv_init(&lcmd->gen, ld_cmd_free) < 0) goto error;
  88. switch(cmd->type) {
  89. case DB_PUT:
  90. case DB_DEL:
  91. case DB_UPD:
  92. ERR("ldap: The driver does not support directory modifications yet.\n");
  93. goto error;
  94. break;
  95. case DB_GET:
  96. break;
  97. case DB_SQL:
  98. ERR("ldap: The driver does not support raw queries yet.\n");
  99. goto error;
  100. }
  101. cfg = ld_find_cfg(&cmd->table);
  102. if (cfg == NULL) {
  103. ERR("ldap: Cannot find configuration for '%.*s', giving up\n",
  104. STR_FMT(&cmd->table));
  105. goto error;
  106. }
  107. lcmd->base = cfg->base.s;
  108. lcmd->scope = cfg->scope;
  109. lcmd->sizelimit = cfg->sizelimit;
  110. if (cfg->timelimit) {
  111. lcmd->timelimit.tv_sec = cfg->timelimit;
  112. lcmd->timelimit.tv_usec = 0;
  113. }
  114. if (cfg->filter.s) {
  115. lcmd->filter = cfg->filter;
  116. }
  117. lcmd->chase_references = cfg->chase_references;
  118. lcmd->chase_referrals = cfg->chase_referrals;
  119. if (ld_resolve_fld(cmd->match, cfg) < 0) goto error;
  120. if (ld_resolve_fld(cmd->result, cfg) < 0) goto error;
  121. /* prepare filter for each result field */
  122. for(i = 0; !DB_FLD_EMPTY(cmd->result) && !DB_FLD_LAST(cmd->result[i]); i++) {
  123. int n;
  124. lfld = DB_GET_PAYLOAD(cmd->result + i);
  125. lfld->filter = NULL;
  126. for(j = 0, n = 0; !DB_FLD_EMPTY(cmd->match) && !DB_FLD_LAST(cmd->match[j]); j++) {
  127. if (strcmp(cmd->result[i].name, cmd->match[j].name) == 0)
  128. n++;
  129. }
  130. if (n > 0) {
  131. lfld->filter = pkg_malloc((n+1)*sizeof(*(lfld->filter)));
  132. if (!lfld->filter) return -1 /* E_OUT_OF_MEM*/;
  133. for(j = 0, n = 0; !DB_FLD_EMPTY(cmd->match) && !DB_FLD_LAST(cmd->match[j]); j++) {
  134. if (strcmp(cmd->result[i].name, cmd->match[j].name) == 0) {
  135. lfld->filter[n] = cmd->match+j;
  136. n++;
  137. }
  138. }
  139. lfld->filter[n] = NULL;
  140. }
  141. }
  142. if (build_result_array(&lcmd->result, cmd) < 0) goto error;
  143. DB_SET_PAYLOAD(cmd, lcmd);
  144. return 0;
  145. error:
  146. if (lcmd) {
  147. DB_SET_PAYLOAD(cmd, NULL);
  148. db_drv_free(&lcmd->gen);
  149. if (lcmd->result) pkg_free(lcmd->result);
  150. pkg_free(lcmd);
  151. }
  152. return -1;
  153. }
  154. int ld_cmd_exec(db_res_t* res, db_cmd_t* cmd)
  155. {
  156. db_con_t* con;
  157. struct ld_res* lres;
  158. struct ld_cmd* lcmd;
  159. struct ld_con* lcon;
  160. char* filter, *err_desc;
  161. int ret, err;
  162. LDAPMessage *msg, *resmsg;
  163. int reconn_cnt;
  164. int msgid;
  165. char *oid;
  166. struct berval *data;
  167. struct timeval restimeout;
  168. filter = NULL;
  169. err_desc = NULL;
  170. resmsg = NULL;
  171. /* First things first: retrieve connection info from the currently active
  172. * connection and also mysql payload from the database command
  173. */
  174. con = cmd->ctx->con[db_payload_idx];
  175. lcmd = DB_GET_PAYLOAD(cmd);
  176. lcon = DB_GET_PAYLOAD(con);
  177. reconn_cnt = ld_reconnect_attempt;
  178. if (ld_prepare_ldap_filter(&filter, cmd, &lcmd->filter) < 0) {
  179. ERR("ldap: Error while building LDAP search filter\n");
  180. goto error;
  181. }
  182. DBG("ldap: ldap_search(base:'%s', filter:'%s')\n", lcmd->base, filter);
  183. do {
  184. if (lcon->flags & LD_CONNECTED) {
  185. ldap_set_option(lcon->con, LDAP_OPT_DEREF, ((void *)&lcmd->chase_references));
  186. /* there is alternative method using LDAP_CONTROL_REFERRALS per request but is not well documented */
  187. ldap_set_option(lcon->con, LDAP_OPT_REFERRALS, lcmd->chase_referrals?LDAP_OPT_ON:LDAP_OPT_OFF);
  188. ret = ldap_search_ext(lcon->con, lcmd->base, lcmd->scope, filter,
  189. lcmd->result, 0, NULL, NULL,
  190. lcmd->timelimit.tv_sec ? &lcmd->timelimit : NULL,
  191. lcmd->sizelimit,
  192. &msgid);
  193. if (ret != LDAP_SUCCESS) {
  194. ERR("ldap: Error while searching: %s\n", ldap_err2string(ret));
  195. goto error;
  196. }
  197. /*
  198. openldap v2.3 library workaround for unsolicited messages:
  199. if only unsolicited messages are available then ldap_result of
  200. v2.3 library waits forever
  201. */
  202. memset(&restimeout, 0, sizeof(restimeout));
  203. restimeout.tv_sec = 5;
  204. ret = ldap_result(lcon->con,
  205. LDAP_RES_ANY,
  206. LDAP_MSG_ALL,
  207. &restimeout,
  208. &resmsg);
  209. } else {
  210. /* force it to reconnect */
  211. ret = -1;
  212. }
  213. if (ret <= 0) {
  214. ERR("ldap: Error in ldap_search: %s\n", ret < 0 ? ldap_err2string(ret) : "timeout");
  215. if (ret == LDAP_SERVER_DOWN) {
  216. lcon->flags &= ~LD_CONNECTED;
  217. do {
  218. if (!reconn_cnt) {
  219. ERR("ldap: maximum reconnection attempt reached! giving up\n");
  220. goto error;
  221. }
  222. reconn_cnt--;
  223. err = ld_con_connect(con);
  224. } while (err != 0);
  225. } else {
  226. goto error;
  227. }
  228. }
  229. } while (ret <= 0);
  230. /* looking for unsolicited messages */
  231. for (msg = ldap_first_message(lcon->con, resmsg);
  232. msg != NULL;
  233. msg = ldap_next_message(lcon->con, msg)) {
  234. if (ldap_msgtype(msg) == LDAP_RES_EXTENDED) {
  235. if (ldap_parse_extended_result(lcon->con,
  236. msg,
  237. &oid,
  238. &data,
  239. 0) != LDAP_SUCCESS) {
  240. ERR("ldap: Error while parsing extended result\n");
  241. goto error;
  242. }
  243. if (oid != NULL) {
  244. if (strcmp(oid, LDAP_NOTICE_OF_DISCONNECTION) == 0) {
  245. WARN("ldap: Notice of Disconnection (OID: %s)\n", oid);
  246. } else {
  247. WARN("ldap: Unsolicited message received. OID: %s\n", oid);
  248. }
  249. ldap_memfree(oid);
  250. }
  251. if (data != NULL) {
  252. WARN("ldap: Unsolicited message data: %.*s\n",
  253. (int)data->bv_len, data->bv_val);
  254. ber_bvfree(data);
  255. }
  256. }
  257. }
  258. ret = ldap_parse_result(lcon->con, resmsg, &err, NULL, &err_desc, NULL, NULL, 0);
  259. if (ret != LDAP_SUCCESS) {
  260. ERR("ldap: Error while reading result status: %s\n",
  261. ldap_err2string(ret));
  262. goto error;
  263. }
  264. if (err != LDAP_SUCCESS) {
  265. ERR("ldap: LDAP server reports error: %s\n", ldap_err2string(err));
  266. goto error;
  267. }
  268. if (res) {
  269. lres = DB_GET_PAYLOAD(res);
  270. lres->msg = resmsg;
  271. } else if (resmsg) {
  272. ldap_msgfree(resmsg);
  273. }
  274. if (filter) pkg_free(filter);
  275. if (err_desc) ldap_memfree(err_desc);
  276. return 0;
  277. error:
  278. if (filter) pkg_free(filter);
  279. if (resmsg) ldap_msgfree(resmsg);
  280. if (err_desc) ldap_memfree(err_desc);
  281. return -1;
  282. }
  283. /* Iterate to the next search result in the linked list
  284. * of messages returned by the LDAP server and convert
  285. * the field values.
  286. */
  287. static int search_entry(db_res_t* res, int init)
  288. {
  289. db_con_t* con;
  290. struct ld_res* lres;
  291. struct ld_con* lcon;
  292. int r;
  293. lres = DB_GET_PAYLOAD(res);
  294. /* FIXME */
  295. con = res->cmd->ctx->con[db_payload_idx];
  296. lcon = DB_GET_PAYLOAD(con);
  297. if (init
  298. || !lres->current
  299. || ldap_msgtype(lres->current) != LDAP_RES_SEARCH_ENTRY
  300. /* there is no more value combination result left */
  301. || ld_incindex(res->cmd->result)) {
  302. do {
  303. if (init) {
  304. lres->current = ldap_first_message(lcon->con, lres->msg);
  305. init = 0;
  306. }
  307. else
  308. lres->current = ldap_next_message(lcon->con, lres->current);
  309. while(lres->current) {
  310. if (ldap_msgtype(lres->current) == LDAP_RES_SEARCH_ENTRY) {
  311. break;
  312. }
  313. lres->current = ldap_next_message(lcon->con, lres->current);
  314. }
  315. if (lres->current == NULL) return 1;
  316. r = ld_ldap2fldinit(res->cmd->result, lcon->con, lres->current);
  317. } while (r > 0);
  318. if (r < 0) return -1;
  319. } else {
  320. if (ld_ldap2fld(res->cmd->result, lcon->con, lres->current) < 0) return -1;
  321. }
  322. res->cur_rec->fld = res->cmd->result;
  323. return 0;
  324. }
  325. int ld_cmd_first(db_res_t* res)
  326. {
  327. return search_entry(res, 1);
  328. }
  329. int ld_cmd_next(db_res_t* res)
  330. {
  331. return search_entry(res, 0);
  332. }
  333. #define is_space(c) ((c)==' '||(c)==','||(c)==';'||(c)=='\t'||(c)=='\n'||(c)=='\r'||(c)=='\0')
  334. int ld_cmd_setopt(db_cmd_t* cmd, char* optname, va_list ap)
  335. {
  336. struct ld_fld* lfld;
  337. char* val, *c;
  338. int i;
  339. if (!strcasecmp("client_side_filtering", optname)) {
  340. val = va_arg(ap, char*);
  341. for(i = 0; !DB_FLD_EMPTY(cmd->result) && !DB_FLD_LAST(cmd->result[i]); i++) {
  342. c = val;
  343. do {
  344. c = strstr(c, cmd->result[i].name);
  345. if (c) {
  346. if ((c == val || is_space(*(c-1))) && is_space(*(c+strlen(cmd->result[i].name)))) {
  347. lfld = (struct ld_fld*)DB_GET_PAYLOAD(cmd->result + i);
  348. lfld->client_side_filtering = 1;
  349. break;
  350. }
  351. c += strlen(cmd->result[i].name);
  352. }
  353. } while (c != NULL);
  354. }
  355. }
  356. else
  357. return 1;
  358. return 0;
  359. }
  360. /** @} */