master-auth.c revision 3b22894b8805b186c73d8b754001e8d7e944be85
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "lib.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "ioloop.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "fdpass.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "buffer.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "hash.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "master-service-private.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "master-auth.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include <stdlib.h>
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include <unistd.h>
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include <sys/stat.h>
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen#define SOCKET_CONNECT_RETRY_MSECS 500
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstruct master_auth_connection {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth *auth;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen unsigned int tag;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen int fd;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct io *io;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen char buf[sizeof(struct master_auth_reply)];
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen unsigned int buf_pos;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen master_auth_callback_t *callback;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen void *context;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen};
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstruct master_auth {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_service *service;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen pool_t pool;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const char *path;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen unsigned int tag_counter;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct hash_table *connections;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen};
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstruct master_auth *
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenmaster_auth_init(struct master_service *service, const char *path)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth *auth;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen pool_t pool;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen pool = pool_alloconly_create("master auth", 1024);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen auth = p_new(pool, struct master_auth, 1);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen auth->pool = pool;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen auth->service = service;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen auth->path = p_strdup(pool, path);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen auth->connections = hash_table_create(default_pool, pool,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen 0, NULL, NULL);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen return auth;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstatic void
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenmaster_auth_connection_deinit(struct master_auth_connection **_conn)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth_connection *conn = *_conn;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen *_conn = NULL;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (conn->tag != 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen hash_table_remove(conn->auth->connections,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen POINTER_CAST(conn->tag));
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen if (conn->callback != NULL)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen conn->callback(NULL, conn->context);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (conn->io != NULL)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen io_remove(&conn->io);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (conn->fd != -1) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (close(conn->fd) < 0)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_fatal("close(master auth conn) failed: %m");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->fd = -1;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen }
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_free(conn);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen}
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenvoid master_auth_deinit(struct master_auth **_auth)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth *auth = *_auth;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct hash_iterate_context *iter;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen void *key, *value;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen *_auth = NULL;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen iter = hash_table_iterate_init(auth->connections);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen while (hash_table_iterate(iter, &key, &value)) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth_connection *conn = value;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->tag = 0;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen master_auth_connection_deinit(&conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen hash_table_iterate_deinit(&iter);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen hash_table_destroy(&auth->connections);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen pool_unref(&auth->pool);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstatic void master_auth_connection_input(struct master_auth_connection *conn)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const struct master_auth_reply *reply;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen int ret;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen ret = read(conn->fd, conn->buf + conn->buf_pos,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen sizeof(conn->buf) - conn->buf_pos);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (ret <= 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (ret < 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (errno == EAGAIN)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen return;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("read(master auth conn) failed: %m");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen } else {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("read(master auth conn) failed: "
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen "Remote closed connection");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen master_auth_connection_deinit(&conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->buf_pos += ret;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (conn->buf_pos < sizeof(conn->buf))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* reply is now read */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen reply = (struct master_auth_reply *)conn->buf;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->buf_pos = 0;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (conn->tag != reply->tag)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("Master sent reply with unknown tag %u", reply->tag);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen else {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->callback(reply, conn->context);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->callback = NULL;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen master_auth_connection_deinit(&conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenvoid master_auth_request(struct master_auth *auth, int fd,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const struct master_auth_request *request,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen const unsigned char *data,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen master_auth_callback_t *callback,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen void *context, unsigned int *tag_r)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth_connection *conn;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth_request req;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen buffer_t *buf;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct stat st;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen ssize_t ret;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_assert(request->client_pid != 0);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_assert(request->auth_pid != 0);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn = i_new(struct master_auth_connection, 1);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->auth = auth;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->callback = callback;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->context = context;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen req = *request;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen req.tag = ++auth->tag_counter;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (req.tag == 0)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen req.tag = ++auth->tag_counter;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (fstat(fd, &st) < 0)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_fatal("fstat(auth dest fd) failed: %m");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen req.ino = st.st_ino;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(),
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen sizeof(req) + req.data_size);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen buffer_append(buf, &req, sizeof(req));
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen buffer_append(buf, data, req.data_size);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen conn->fd = net_connect_unix_with_retries(auth->path,
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen SOCKET_CONNECT_RETRY_MSECS);
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen if (conn->fd == -1) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("net_connect_unix(%s) failed: %m", auth->path);
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen master_auth_connection_deinit(&conn);
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen return;
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen }
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
405fbb38e0d4bd33a82b547dc9590245885f0b62Timo Sirainen ret = fd_send(conn->fd, fd, buf->data, buf->used);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (ret < 0)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("fd_send(%d) failed: %m", fd);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen else if ((size_t)ret != buf->used) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("fd_send() sent only %d of %d bytes",
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen (int)ret, (int)buf->used);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen ret = -1;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (ret < 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen master_auth_connection_deinit(&conn);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen return;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->tag = req.tag;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->io = io_add(conn->fd, IO_READ,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen master_auth_connection_input, conn);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen *tag_r = req.tag;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenvoid master_auth_request_abort(struct master_auth *auth, unsigned int tag)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_auth_connection *conn;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn = hash_table_lookup(auth->connections, POINTER_CAST(tag));
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (conn == NULL)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_panic("master_auth_request_abort(): tag %u not found", tag);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->callback = NULL;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}