proto.c revision 218192
180709Sjake/*-
280709Sjake * Copyright (c) 2009-2010 The FreeBSD Foundation
380709Sjake * All rights reserved.
480709Sjake *
580709Sjake * This software was developed by Pawel Jakub Dawidek under sponsorship from
680709Sjake * the FreeBSD Foundation.
780709Sjake *
880709Sjake * Redistribution and use in source and binary forms, with or without
980709Sjake * modification, are permitted provided that the following conditions
1080709Sjake * are met:
1180709Sjake * 1. Redistributions of source code must retain the above copyright
1280709Sjake *    notice, this list of conditions and the following disclaimer.
1380709Sjake * 2. Redistributions in binary form must reproduce the above copyright
1481337Sobrien *    notice, this list of conditions and the following disclaimer in the
1580709Sjake *    documentation and/or other materials provided with the distribution.
1680709Sjake *
1781337Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1880709Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1980709Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2080709Sjake * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2180709Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2280709Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2380709Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2480709Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2580709Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2680709Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2780709Sjake * SUCH DAMAGE.
2880709Sjake */
2980709Sjake
3080709Sjake#include <sys/cdefs.h>
3180709Sjake__FBSDID("$FreeBSD: head/sbin/hastd/proto.c 218192 2011-02-02 15:42:00Z pjd $");
3280709Sjake
3382910Sjake#include <sys/types.h>
3480709Sjake#include <sys/queue.h>
3580709Sjake#include <sys/socket.h>
3686527Sjake
3791613Sjake#include <errno.h>
3891613Sjake#include <stdint.h>
3980709Sjake#include <strings.h>
4091176Sjake
4184186Sjake#include "pjdlog.h"
4280709Sjake#include "proto.h"
4380709Sjake#include "proto_impl.h"
4481381Sjake
4581381Sjake#define	PROTO_CONN_MAGIC	0x907041c
4681381Sjakestruct proto_conn {
4781381Sjake	int			 pc_magic;
4888657Sjake	struct hast_proto	*pc_proto;
4980709Sjake	void			*pc_ctx;
5080709Sjake	int			 pc_side;
5180709Sjake#define	PROTO_SIDE_CLIENT		0
5280709Sjake#define	PROTO_SIDE_SERVER_LISTEN	1
5381135Stmm#define	PROTO_SIDE_SERVER_WORK		2
5480709Sjake};
5586527Sjake
5681614Sjakestatic TAILQ_HEAD(, hast_proto) protos = TAILQ_HEAD_INITIALIZER(protos);
5788657Sjake
5880709Sjakevoid
5980709Sjakeproto_register(struct hast_proto *proto, bool isdefault)
6080709Sjake{
6182910Sjake	static bool seen_default = false;
6289052Sjake
6380709Sjake	if (!isdefault)
6480709Sjake		TAILQ_INSERT_HEAD(&protos, proto, hp_next);
6580709Sjake	else {
6680709Sjake		PJDLOG_ASSERT(!seen_default);
6782010Sjake		seen_default = true;
6888788Sjake		TAILQ_INSERT_TAIL(&protos, proto, hp_next);
6980709Sjake	}
7083756Sjake}
7183756Sjake
7280709Sjakestatic struct proto_conn *
7380709Sjakeproto_alloc(struct hast_proto *proto, int side)
7480709Sjake{
7583366Sjulian	struct proto_conn *conn;
7684186Sjake
7791360Sjake	PJDLOG_ASSERT(proto != NULL);
7883366Sjulian	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
7980709Sjake	    side == PROTO_SIDE_SERVER_LISTEN ||
8080709Sjake	    side == PROTO_SIDE_SERVER_WORK);
8182910Sjake
8280709Sjake	conn = malloc(sizeof(*conn));
8381135Stmm	if (conn != NULL) {
8481135Stmm		conn->pc_proto = proto;
8581135Stmm		conn->pc_side = side;
8681135Stmm		conn->pc_magic = PROTO_CONN_MAGIC;
8788657Sjake	}
8888657Sjake	return (conn);
8991224Sjake}
9091224Sjake
9181381Sjakestatic void
9289052Sjakeproto_free(struct proto_conn *conn)
9389052Sjake{
9488657Sjake
9581381Sjake	PJDLOG_ASSERT(conn != NULL);
9689052Sjake	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
9789052Sjake	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT ||
9889052Sjake	    conn->pc_side == PROTO_SIDE_SERVER_LISTEN ||
9988657Sjake	    conn->pc_side == PROTO_SIDE_SERVER_WORK);
10088657Sjake	PJDLOG_ASSERT(conn->pc_proto != NULL);
10191224Sjake
10280709Sjake	bzero(conn, sizeof(*conn));
10391336Sjake	free(conn);
10491336Sjake}
10591336Sjake
10680709Sjakestatic int
10780709Sjakeproto_common_setup(const char *addr, struct proto_conn **connp, int side)
10880709Sjake{
10989052Sjake	struct hast_proto *proto;
11089052Sjake	struct proto_conn *conn;
11189052Sjake	void *ctx;
11289052Sjake	int ret;
11389052Sjake
11489052Sjake	PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT ||
11589052Sjake	    side == PROTO_SIDE_SERVER_LISTEN);
11689052Sjake
11785244Sjake	TAILQ_FOREACH(proto, &protos, hp_next) {
11884186Sjake		if (side == PROTO_SIDE_CLIENT) {
11989052Sjake			if (proto->hp_client == NULL)
12084186Sjake				ret = -1;
12184186Sjake			else
12282910Sjake				ret = proto->hp_client(addr, &ctx);
12385244Sjake		} else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ {
12485244Sjake			if (proto->hp_server == NULL)
12585244Sjake				ret = -1;
12691532Sjake			else
12791532Sjake				ret = proto->hp_server(addr, &ctx);
12891532Sjake		}
12991532Sjake		/*
13082910Sjake		 * ret == 0  - success
13182910Sjake		 * ret == -1 - addr is not for this protocol
13282910Sjake		 * ret > 0   - right protocol, but an error occured
13382910Sjake		 */
13482910Sjake		if (ret >= 0)
13582910Sjake			break;
13682910Sjake	}
13782910Sjake	if (proto == NULL) {
13888657Sjake		/* Unrecognized address. */
13982910Sjake		errno = EINVAL;
14091224Sjake		return (-1);
14180709Sjake	}
14281381Sjake	if (ret > 0) {
14380709Sjake		/* An error occured. */
14481614Sjake		errno = ret;
14580709Sjake		return (-1);
14688657Sjake	}
14788657Sjake	conn = proto_alloc(proto, side);
14880709Sjake	if (conn == NULL) {
14980709Sjake		if (proto->hp_close != NULL)
15084186Sjake			proto->hp_close(ctx);
15184186Sjake		errno = ENOMEM;
15287702Sjhb		return (-1);
15387702Sjhb	}
15487702Sjhb	conn->pc_ctx = ctx;
15591337Sjake	*connp = conn;
15689052Sjake
15789052Sjake	return (0);
15891613Sjake}
15991613Sjake
16091613Sjakeint
16189052Sjakeproto_client(const char *addr, struct proto_conn **connp)
16280709Sjake{
16384186Sjake
16484186Sjake	return (proto_common_setup(addr, connp, PROTO_SIDE_CLIENT));
16581614Sjake}
16681614Sjake
16781614Sjakeint
16881614Sjakeproto_connect(struct proto_conn *conn, int timeout)
16981614Sjake{
17081614Sjake	int ret;
17181614Sjake
17281614Sjake	PJDLOG_ASSERT(conn != NULL);
17381614Sjake	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
17481614Sjake	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT);
17581614Sjake	PJDLOG_ASSERT(conn->pc_proto != NULL);
17689052Sjake	PJDLOG_ASSERT(conn->pc_proto->hp_connect != NULL);
17789052Sjake	PJDLOG_ASSERT(timeout >= 0);
17889052Sjake
17989052Sjake	ret = conn->pc_proto->hp_connect(conn->pc_ctx, timeout);
18089052Sjake	if (ret != 0) {
18189052Sjake		errno = ret;
18289052Sjake		return (-1);
18389052Sjake	}
18481614Sjake
18581614Sjake	return (0);
18681614Sjake}
18781614Sjake
18881614Sjakeint
18985244Sjakeproto_server(const char *addr, struct proto_conn **connp)
19085244Sjake{
19183366Sjulian
19283366Sjulian	return (proto_common_setup(addr, connp, PROTO_SIDE_SERVER_LISTEN));
19382910Sjake}
19488788Sjake
19588788Sjakeint
19691613Sjakeproto_accept(struct proto_conn *conn, struct proto_conn **newconnp)
19791613Sjake{
19882910Sjake	struct proto_conn *newconn;
19988657Sjake	int ret;
20088657Sjake
20182910Sjake	PJDLOG_ASSERT(conn != NULL);
20280709Sjake	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
20380709Sjake	PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN);
20488657Sjake	PJDLOG_ASSERT(conn->pc_proto != NULL);
20588657Sjake	PJDLOG_ASSERT(conn->pc_proto->hp_accept != NULL);
20683366Sjulian
20783366Sjulian	newconn = proto_alloc(conn->pc_proto, PROTO_SIDE_SERVER_WORK);
20883366Sjulian	if (newconn == NULL)
20983366Sjulian		return (-1);
21083366Sjulian
21183366Sjulian	ret = conn->pc_proto->hp_accept(conn->pc_ctx, &newconn->pc_ctx);
21283366Sjulian	if (ret != 0) {
21383366Sjulian		proto_free(newconn);
21484186Sjake		errno = ret;
21581135Stmm		return (-1);
21680709Sjake	}
21780709Sjake
21882010Sjake	*newconnp = newconn;
21980709Sjake
22082910Sjake	return (0);
22182910Sjake}
22282910Sjake
22380709Sjakeint
22481381Sjakeproto_send(const struct proto_conn *conn, const void *data, size_t size)
22588657Sjake{
22681381Sjake	int ret;
22788657Sjake
22881381Sjake	PJDLOG_ASSERT(conn != NULL);
22981135Stmm	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
23081135Stmm	PJDLOG_ASSERT(conn->pc_proto != NULL);
23181135Stmm	PJDLOG_ASSERT(conn->pc_proto->hp_send != NULL);
23281135Stmm
23381135Stmm	ret = conn->pc_proto->hp_send(conn->pc_ctx, data, size);
23480709Sjake	if (ret != 0) {
23580709Sjake		errno = ret;
23680709Sjake		return (-1);
23782910Sjake	}
23882010Sjake	return (0);
23988657Sjake}
24088657Sjake
24188657Sjakeint
24288657Sjakeproto_recv(const struct proto_conn *conn, void *data, size_t size)
24388657Sjake{
24480709Sjake	int ret;
24580709Sjake
24680709Sjake	PJDLOG_ASSERT(conn != NULL);
24780709Sjake	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
24880709Sjake	PJDLOG_ASSERT(conn->pc_proto != NULL);
24980709Sjake	PJDLOG_ASSERT(conn->pc_proto->hp_recv != NULL);
25080709Sjake
25180709Sjake	ret = conn->pc_proto->hp_recv(conn->pc_ctx, data, size);
25280709Sjake	if (ret != 0) {
25380709Sjake		errno = ret;
25480709Sjake		return (-1);
25580709Sjake	}
25680709Sjake	return (0);
25780709Sjake}
25880709Sjake
25980709Sjakeint
26080709Sjakeproto_descriptor_send(const struct proto_conn *conn, int fd)
26188657Sjake{
26288657Sjake	int ret;
26388657Sjake
26488657Sjake	PJDLOG_ASSERT(conn != NULL);
26588657Sjake	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
26688657Sjake	PJDLOG_ASSERT(conn->pc_proto != NULL);
26780709Sjake	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor_send != NULL);
26888657Sjake
26980709Sjake	ret = conn->pc_proto->hp_descriptor_send(conn->pc_ctx, fd);
27088657Sjake	if (ret != 0) {
27188657Sjake		errno = ret;
27288657Sjake		return (-1);
27382910Sjake	}
27480709Sjake	return (0);
27588788Sjake}
27688788Sjake
277int
278proto_descriptor_recv(const struct proto_conn *conn, int *fdp)
279{
280	int ret;
281
282	PJDLOG_ASSERT(conn != NULL);
283	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
284	PJDLOG_ASSERT(conn->pc_proto != NULL);
285	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor_recv != NULL);
286
287	ret = conn->pc_proto->hp_descriptor_recv(conn->pc_ctx, fdp);
288	if (ret != 0) {
289		errno = ret;
290		return (-1);
291	}
292	return (0);
293}
294
295int
296proto_descriptor(const struct proto_conn *conn)
297{
298
299	PJDLOG_ASSERT(conn != NULL);
300	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
301	PJDLOG_ASSERT(conn->pc_proto != NULL);
302	PJDLOG_ASSERT(conn->pc_proto->hp_descriptor != NULL);
303
304	return (conn->pc_proto->hp_descriptor(conn->pc_ctx));
305}
306
307bool
308proto_address_match(const struct proto_conn *conn, const char *addr)
309{
310
311	PJDLOG_ASSERT(conn != NULL);
312	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
313	PJDLOG_ASSERT(conn->pc_proto != NULL);
314	PJDLOG_ASSERT(conn->pc_proto->hp_address_match != NULL);
315
316	return (conn->pc_proto->hp_address_match(conn->pc_ctx, addr));
317}
318
319void
320proto_local_address(const struct proto_conn *conn, char *addr, size_t size)
321{
322
323	PJDLOG_ASSERT(conn != NULL);
324	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
325	PJDLOG_ASSERT(conn->pc_proto != NULL);
326	PJDLOG_ASSERT(conn->pc_proto->hp_local_address != NULL);
327
328	conn->pc_proto->hp_local_address(conn->pc_ctx, addr, size);
329}
330
331void
332proto_remote_address(const struct proto_conn *conn, char *addr, size_t size)
333{
334
335	PJDLOG_ASSERT(conn != NULL);
336	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
337	PJDLOG_ASSERT(conn->pc_proto != NULL);
338	PJDLOG_ASSERT(conn->pc_proto->hp_remote_address != NULL);
339
340	conn->pc_proto->hp_remote_address(conn->pc_ctx, addr, size);
341}
342
343int
344proto_timeout(const struct proto_conn *conn, int timeout)
345{
346	struct timeval tv;
347	int fd;
348
349	PJDLOG_ASSERT(conn != NULL);
350	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
351	PJDLOG_ASSERT(conn->pc_proto != NULL);
352
353	fd = proto_descriptor(conn);
354	if (fd < 0)
355		return (-1);
356
357	tv.tv_sec = timeout;
358	tv.tv_usec = 0;
359	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
360		return (-1);
361	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
362		return (-1);
363
364	return (0);
365}
366
367void
368proto_close(struct proto_conn *conn)
369{
370
371	PJDLOG_ASSERT(conn != NULL);
372	PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC);
373	PJDLOG_ASSERT(conn->pc_proto != NULL);
374	PJDLOG_ASSERT(conn->pc_proto->hp_close != NULL);
375
376	conn->pc_proto->hp_close(conn->pc_ctx);
377	proto_free(conn);
378}
379