sresolv.c 8.84 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
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
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2006 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@CFILE sresolv.c
 * @brief Sofia DNS Resolver interface using su_root_t.
27
 *
Pekka Pessi's avatar
Pekka Pessi committed
28 29 30 31 32 33 34
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Teemu Jalava <Teemu.Jalava@nokia.com>
 * @author Mikko Haataja
 *
 * @todo The resolver should allow handling arbitrary records, too.
 */

35
#include "config.h"
Pekka Pessi's avatar
Pekka Pessi committed
36

37 38 39
#define SU_TIMER_ARG_T  struct sres_sofia_s
#define SU_WAKEUP_ARG_T struct sres_sofia_register_s
#define SRES_ASYNC_T    struct sres_sofia_s
Pekka Pessi's avatar
Pekka Pessi committed
40

41
#include <sofia-sip/sresolv.h>
Pekka Pessi's avatar
Pekka Pessi committed
42

43 44
#define SU_LOG sresolv_log
#include <sofia-sip/su_debug.h>
Pekka Pessi's avatar
Pekka Pessi committed
45

46 47
#include <string.h>
#include <assert.h>
Pekka Pessi's avatar
Pekka Pessi committed
48 49 50 51 52 53

/* ====================================================================== */
/* Glue functions for Sofia root (reactor) */

#define TAG_NAMESPACE "sres"

54 55
#include <sofia-sip/su_tag_class.h>
#include <sofia-sip/su_tagarg.h>
Pekka Pessi's avatar
Pekka Pessi committed
56

57
tag_typedef_t srestag_any = NSTAG_TYPEDEF(*);
Pekka Pessi's avatar
Pekka Pessi committed
58 59 60
tag_typedef_t srestag_resolv_conf = STRTAG_TYPEDEF(resolv_conf);
tag_typedef_t srestag_resolv_conf_ref = REFTAG_TYPEDEF(srestag_resolv_conf);

61 62 63
tag_typedef_t srestag_cache = PTRTAG_TYPEDEF(cache);
tag_typedef_t srestag_cache_ref = REFTAG_TYPEDEF(srestag_cache);

64 65 66 67
typedef struct sres_sofia_s sres_sofia_t;
typedef struct sres_sofia_register_s sres_sofia_register_t;

struct sres_sofia_register_s {
68
  sres_sofia_t *reg_ptr;
69
  su_socket_t reg_socket;
70
  int reg_index;		/**< Registration index */
71 72 73
};

struct sres_sofia_s {
Pekka Pessi's avatar
Pekka Pessi committed
74 75 76
  sres_resolver_t *srs_resolver;
  su_root_t  	  *srs_root;
  su_timer_t 	  *srs_timer;
77
  su_socket_t      srs_socket;
78 79 80
  sres_sofia_register_t srs_reg[SRES_MAX_NAMESERVERS];
};

81
static int sres_sofia_update(sres_sofia_t *,
82 83
			     su_socket_t new_socket,
			     su_socket_t old_socket);
Pekka Pessi's avatar
Pekka Pessi committed
84

85
static void sres_sofia_timer(su_root_magic_t *magic,
Pekka Pessi's avatar
Pekka Pessi committed
86 87 88
			     su_timer_t *t,
			     sres_sofia_t *arg);

89 90
static int sres_sofia_set_timer(sres_sofia_t *srs, unsigned long interval);

91
static int sres_sofia_poll(su_root_magic_t *, su_wait_t *,
92
			   sres_sofia_register_t *);
Pekka Pessi's avatar
Pekka Pessi committed
93 94 95 96 97 98 99

/**Create a resolver.
 *
 * The function sres_resolver_create() is used to allocate and initialize
 * the resolver object using the Sofia asynchronous reactor #su_root_t.
 */
sres_resolver_t *
100
sres_resolver_create(su_root_t *root,
Pekka Pessi's avatar
Pekka Pessi committed
101 102 103 104 105
		     char const *conf_file_path,
		     tag_type_t tag, tag_value_t value, ...)
{
  sres_resolver_t *res;
  sres_sofia_t *srs;
106
  sres_cache_t *cache = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
107 108
  ta_list ta;

109
  if (root == NULL)
110
    return su_seterrno(EFAULT), (void *)NULL;
Pekka Pessi's avatar
Pekka Pessi committed
111 112 113 114

  ta_start(ta, tag, value);
  tl_gets(ta_args(ta),
	  SRESTAG_RESOLV_CONF_REF(conf_file_path),
115
	  SRESTAG_CACHE_REF(cache),
Pekka Pessi's avatar
Pekka Pessi committed
116 117 118
	  TAG_END());
  ta_end(ta);

119
  res = sres_resolver_new_with_cache(conf_file_path, cache, NULL);
120
  srs = res ? su_zalloc(0, sizeof *srs) : NULL;
Pekka Pessi's avatar
Pekka Pessi committed
121 122

  if (res && srs) {
123
    su_timer_t *t;
Pekka Pessi's avatar
Pekka Pessi committed
124 125 126

    srs->srs_resolver = res;
    srs->srs_root = root;
127
    srs->srs_socket = INVALID_SOCKET;
Pekka Pessi's avatar
Pekka Pessi committed
128

129
    sres_resolver_set_async(res, sres_sofia_update, srs, 0);
130

131 132 133 134 135
    t = su_timer_create(su_root_task(root), SRES_RETRANSMIT_INTERVAL);
    srs->srs_timer = t;

    if (!srs->srs_timer)
      SU_DEBUG_3(("sres: cannot create timer\n"));
136
#if nomore
137 138
    else if (su_timer_set_for_ever(t, sres_sofia_timer, srs) < 0)
      SU_DEBUG_3(("sres: cannot set timer\n"));
139 140 141 142
#else
    else if (sres_resolver_set_timer_cb(res, sres_sofia_set_timer, srs) < 0)
      SU_DEBUG_3(("sres: cannot set timer cb\n"));
#endif
143 144
    else
      return res;		/* Success! */
Pekka Pessi's avatar
Pekka Pessi committed
145

146
    sres_resolver_destroy(res), res = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
147 148 149 150 151 152
  }

  return res;
}

/** Destroy a resolver object. */
153
int
Pekka Pessi's avatar
Pekka Pessi committed
154 155
sres_resolver_destroy(sres_resolver_t *res)
{
156 157 158 159
  sres_sofia_t *srs;

  if (res == NULL)
    return su_seterrno(EFAULT);
Pekka Pessi's avatar
Pekka Pessi committed
160

161
  srs = sres_resolver_get_async(res, sres_sofia_update);
162 163
  if (srs == NULL)
    return su_seterrno(EINVAL);
Pekka Pessi's avatar
Pekka Pessi committed
164

165
  /* Remove sockets from too, zap timers. */
166
  sres_sofia_update(srs, INVALID_SOCKET, INVALID_SOCKET);
167

168
  sres_resolver_unref(res);
169 170

  return 0;
Pekka Pessi's avatar
Pekka Pessi committed
171 172
}

173
/**Update registered socket.
174
 *
175 176
 * @retval 0 if success
 * @retval -1 upon failure
177
 */
178
static int sres_sofia_update(sres_sofia_t *srs,
179 180
			     su_socket_t new_socket,
			     su_socket_t old_socket)
181 182 183
{
  char const *what = NULL;
  su_wait_t wait[1];
184 185
  sres_sofia_register_t *reg = NULL;
  sres_sofia_register_t *old_reg = NULL;
186 187 188
  int i, index = -1, error = 0;
  int N = SRES_MAX_NAMESERVERS;

189 190
  SU_DEBUG_9(("sres_sofia_update(%p, %d, %d)\n",
	      (void *)srs, (int)new_socket, (int)old_socket));
191 192 193 194

  if (srs == NULL)
    return 0;

195 196 197
  if (srs->srs_root == NULL)
    return -1;

198
  if (old_socket == new_socket) {
199
    if (old_socket == INVALID_SOCKET) {
200
      sres_resolver_set_async(srs->srs_resolver, sres_sofia_update, NULL, 0);
201 202
      /* Destroy srs */
      for (i = 0; i < N; i++) {
203
	if (!srs->srs_reg[i].reg_index)
204
	  continue;
205
	su_root_deregister(srs->srs_root, srs->srs_reg[i].reg_index);
206 207 208 209 210
	memset(&srs->srs_reg[i], 0, sizeof(srs->srs_reg[i]));
      }
      su_timer_destroy(srs->srs_timer), srs->srs_timer = NULL;
      su_free(NULL, srs);
    }
211
    return 0;
212
  }
Pekka Pessi's avatar
Pekka Pessi committed
213

214
  if (old_socket != INVALID_SOCKET)
215
    for (i = 0; i < N; i++)
216
      if ((srs->srs_reg + i)->reg_socket == old_socket) {
217
	old_reg = srs->srs_reg + i;
218 219
	break;
      }
Pekka Pessi's avatar
Pekka Pessi committed
220

221
  if (new_socket != INVALID_SOCKET) {
222
    if (old_reg == NULL) {
223
      for (i = 0; i < N; i++) {
224
	if (!(srs->srs_reg + i)->reg_ptr)
225 226 227 228 229
	  break;
      }
      if (i > N)
	return su_seterrno(ENOMEM);

230
      reg = srs->srs_reg + i;
231
    }
232
    else
233
      reg = old_reg;
234 235
  }

236
  if (reg) {
237
    if (su_wait_create(wait, new_socket, SU_WAIT_IN | SU_WAIT_ERR) == -1) {
238
      reg = NULL;
239 240 241 242
      what = "su_wait_create";
      error = su_errno();
    }

243 244
    if (reg)
      index = su_root_register(srs->srs_root, wait, sres_sofia_poll, reg, 0);
245 246

    if (index < 0) {
247
      reg = NULL;
248 249 250 251 252 253
      what = "su_root_register";
      error = su_errno();
      su_wait_destroy(wait);
    }
  }

254
  if (old_reg) {
255 256
    if (old_socket == srs->srs_socket)
      srs->srs_socket = INVALID_SOCKET;
257 258
    su_root_deregister(srs->srs_root, old_reg->reg_index);
    memset(old_reg, 0, sizeof *old_reg);
259 260
  }

261
  if (reg) {
262
    srs->srs_socket = new_socket;
263 264 265 266

    reg->reg_ptr = srs;
    reg->reg_socket = new_socket;
    reg->reg_index = index;
267 268 269 270 271 272 273
  }

  if (!what)
    return 0;		/* success */

  SU_DEBUG_3(("sres: %s: %s\n", what, su_strerror(error)));

274
  return su_seterrno(error);
Pekka Pessi's avatar
Pekka Pessi committed
275 276 277
}


278
/** Return a socket registered to su_root_t object.
279 280
 *
 * @retval sockfd if succesful
281
 * @retval INVALID_SOCKET (-1) upon an error
282 283 284 285 286
 *
 * @ERRORS
 * @ERROR EFAULT Invalid argument passed.
 * @ERROR EINVAL Resolver is not using su_root_t.
 */
287
su_socket_t sres_resolver_root_socket(sres_resolver_t *res)
288 289 290 291 292
{
  sres_sofia_t *srs;
  int i, N = SRES_MAX_NAMESERVERS;

  if (res == NULL)
293
    return (void)su_seterrno(EFAULT), INVALID_SOCKET;
294

295
  srs = sres_resolver_get_async(res, sres_sofia_update);
296 297 298 299

  if (!srs)
    return su_seterrno(EINVAL);

300
  if (sres_resolver_set_async(res, sres_sofia_update, srs, 1) == NULL)
301
    return INVALID_SOCKET;
302

303
  if (srs->srs_socket != INVALID_SOCKET)
304 305 306
    return srs->srs_socket;

  for (i = 0; i < N; i++) {
307
    if (!srs->srs_reg[i].reg_ptr)
308 309 310
      break;
  }

311 312 313 314
  if (i < N) {
    srs->srs_socket = srs->srs_reg[i].reg_socket;
  }
  else {
315
    su_socket_t socket;
316
    if (sres_resolver_sockets(res, &socket, 1) < 0)
317
      return INVALID_SOCKET;
318
  }
319

320
  return srs->srs_socket;
321 322 323
}


Pekka Pessi's avatar
Pekka Pessi committed
324
/** Sofia timer wrapper. */
325 326
static
void
Pekka Pessi's avatar
Pekka Pessi committed
327 328
sres_sofia_timer(su_root_magic_t *magic, su_timer_t *t, sres_sofia_t *srs)
{
329
  sres_resolver_timer(srs->srs_resolver, -1);
Pekka Pessi's avatar
Pekka Pessi committed
330 331
}

332
/** Sofia timer set wrapper. */
333
static
334 335 336 337 338 339 340 341 342
int
sres_sofia_set_timer(sres_sofia_t *srs, unsigned long interval)
{
  if (interval > SU_DURATION_MAX)
    interval = SU_DURATION_MAX;
  return su_timer_set_interval(srs->srs_timer, sres_sofia_timer, srs,
			       (su_duration_t)interval);
}

343

344
/** Sofia poll/select wrapper, called by su_root_t object */
345 346 347 348
static
int
sres_sofia_poll(su_root_magic_t *magic,
		su_wait_t *w,
349
		sres_sofia_register_t *reg)
Pekka Pessi's avatar
Pekka Pessi committed
350
{
351
  sres_sofia_t *srs = reg->reg_ptr;
Pekka Pessi's avatar
Pekka Pessi committed
352
  int retval = 0;
353
  su_socket_t socket = reg->reg_socket;
354
  int events = su_wait_events(w, socket);
Pekka Pessi's avatar
Pekka Pessi committed
355 356

  if (events & SU_WAIT_ERR)
357 358 359
    retval = sres_resolver_error(srs->srs_resolver, socket);
  if (events & SU_WAIT_IN)
    retval = sres_resolver_receive(srs->srs_resolver, socket);
Pekka Pessi's avatar
Pekka Pessi committed
360 361 362

  return retval;
}